diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/cmd | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-upstream/1.1_hg20130304.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/cmd')
364 files changed, 28996 insertions, 21029 deletions
diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index 512fb5a95..c9fdf05d5 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -52,7 +52,7 @@ %token <lval> LTYPE6 LTYPE7 LTYPE8 LTYPE9 LTYPEA %token <lval> LTYPEB LTYPEC LTYPED LTYPEE LTYPEF %token <lval> LTYPEG LTYPEH LTYPEI LTYPEJ LTYPEK -%token <lval> LTYPEL LTYPEM LTYPEN LTYPEBX +%token <lval> LTYPEL LTYPEM LTYPEN LTYPEBX LTYPEPLD %token <lval> LCONST LSP LSB LFP LPC %token <lval> LTYPEX LR LREG LF LFREG LC LCREG LPSR LFCR %token <lval> LCOND LS LAT @@ -286,22 +286,30 @@ inst: outcode(AWORD, Always, &nullgen, NREG, &g); } /* - * MULL hi,lo,r1,r2 + * MULL r1,r2,(hi,lo) */ | LTYPEM cond reg ',' reg ',' regreg { outcode($1, $2, &$3, $5.reg, &$7); } /* - * MULA hi,lo,r1,r2 + * MULA r1,r2,r3,r4: (r1*r2+r3) & 0xffffffff -> r4 + * MULAW{T,B} r1,r2,r3,r4 */ | LTYPEN cond reg ',' reg ',' reg ',' spreg { - $7.type = D_REGREG; + $7.type = D_REGREG2; $7.offset = $9; outcode($1, $2, &$3, $5.reg, &$7); } /* + * PLD + */ +| LTYPEPLD oreg + { + outcode($1, Always, &$2, NREG, &nullgen); + } +/* * END */ | LTYPEE comma diff --git a/src/cmd/5a/doc.go b/src/cmd/5a/doc.go index a0d2c4c64..29725db04 100644 --- a/src/cmd/5a/doc.go +++ b/src/cmd/5a/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5a is a version of the Plan 9 assembler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2a + http://plan9.bell-labs.com/magic/man2html/1/8a Its target architecture is the ARM, referred to by these tools as arm. */ -package documentation +package main diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index 4bef0219a..a77e3050d 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -329,8 +329,6 @@ struct "STREXD", LTYPE9, ASTREXD, /* - "ABSF", LTYPEI, AABSF, - "ABSD", LTYPEI, AABSD, "NEGF", LTYPEI, ANEGF, "NEGD", LTYPEI, ANEGD, "SQTF", LTYPEI, ASQTF, @@ -343,6 +341,8 @@ struct "NRMD", LTYPEI, ANRMD, */ + "ABSF", LTYPEI, AABSF, + "ABSD", LTYPEI, AABSD, "SQRTF", LTYPEI, ASQRTF, "SQRTD", LTYPEI, ASQRTD, "CMPF", LTYPEL, ACMPF, @@ -403,6 +403,18 @@ struct "MCR", LTYPEJ, 0, "MRC", LTYPEJ, 1, + + "PLD", LTYPEPLD, APLD, + "UNDEF", LTYPEE, AUNDEF, + "CLZ", LTYPE2, ACLZ, + + "MULWT", LTYPE1, AMULWT, + "MULWB", LTYPE1, AMULWB, + "MULAWT", LTYPEN, AMULAWT, + "MULAWB", LTYPEN, AMULAWB, + + "USEFIELD", LTYPEN, AUSEFIELD, + 0 }; @@ -493,6 +505,7 @@ zaddr(Gen *a, int s) Bputc(&obuf, a->reg); Bputc(&obuf, s); Bputc(&obuf, a->name); + Bputc(&obuf, 0); switch(a->type) { default: print("unknown type %d\n", a->type); @@ -506,6 +519,7 @@ zaddr(Gen *a, int s) break; case D_REGREG: + case D_REGREG2: Bputc(&obuf, a->offset); break; @@ -641,11 +655,37 @@ outhist(void) Hist *h; char *p, *q, *op, c; int n; - + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; g = nullgen; c = '/'; for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -697,6 +737,11 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c index a2fba60b3..ce97ee315 100644 --- a/src/cmd/5a/y.tab.c +++ b/src/cmd/5a/y.tab.c @@ -128,28 +128,29 @@ LTYPEM = 279, LTYPEN = 280, LTYPEBX = 281, - LCONST = 282, - LSP = 283, - LSB = 284, - LFP = 285, - LPC = 286, - LTYPEX = 287, - LR = 288, - LREG = 289, - LF = 290, - LFREG = 291, - LC = 292, - LCREG = 293, - LPSR = 294, - LFCR = 295, - LCOND = 296, - LS = 297, - LAT = 298, - LFCONST = 299, - LSCONST = 300, - LNAME = 301, - LLAB = 302, - LVAR = 303 + LTYPEPLD = 282, + LCONST = 283, + LSP = 284, + LSB = 285, + LFP = 286, + LPC = 287, + LTYPEX = 288, + LR = 289, + LREG = 290, + LF = 291, + LFREG = 292, + LC = 293, + LCREG = 294, + LPSR = 295, + LFCR = 296, + LCOND = 297, + LS = 298, + LAT = 299, + LFCONST = 300, + LSCONST = 301, + LNAME = 302, + LLAB = 303, + LVAR = 304 }; #endif /* Tokens. */ @@ -177,28 +178,29 @@ #define LTYPEM 279 #define LTYPEN 280 #define LTYPEBX 281 -#define LCONST 282 -#define LSP 283 -#define LSB 284 -#define LFP 285 -#define LPC 286 -#define LTYPEX 287 -#define LR 288 -#define LREG 289 -#define LF 290 -#define LFREG 291 -#define LC 292 -#define LCREG 293 -#define LPSR 294 -#define LFCR 295 -#define LCOND 296 -#define LS 297 -#define LAT 298 -#define LFCONST 299 -#define LSCONST 300 -#define LNAME 301 -#define LLAB 302 -#define LVAR 303 +#define LTYPEPLD 282 +#define LCONST 283 +#define LSP 284 +#define LSB 285 +#define LFP 286 +#define LPC 287 +#define LTYPEX 288 +#define LR 289 +#define LREG 290 +#define LF 291 +#define LFREG 292 +#define LC 293 +#define LCREG 294 +#define LPSR 295 +#define LFCR 296 +#define LCOND 297 +#define LS 298 +#define LAT 299 +#define LFCONST 300 +#define LSCONST 301 +#define LNAME 302 +#define LLAB 303 +#define LVAR 304 @@ -219,7 +221,7 @@ typedef union YYSTYPE /* Line 214 of yacc.c */ -#line 223 "y.tab.c" +#line 225 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -231,7 +233,7 @@ typedef union YYSTYPE /* Line 264 of yacc.c */ -#line 235 "y.tab.c" +#line 237 "y.tab.c" #ifdef short # undef short @@ -446,20 +448,20 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 643 +#define YYLAST 603 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 69 +#define YYNTOKENS 70 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 35 /* YYNRULES -- Number of rules. */ -#define YYNRULES 129 +#define YYNRULES 130 /* YYNRULES -- Number of states. */ -#define YYNSTATES 327 +#define YYNSTATES 329 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 303 +#define YYMAXUTOK 304 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -470,16 +472,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 67, 12, 5, 2, - 65, 66, 10, 8, 62, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 59, 61, - 6, 60, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 68, 12, 5, 2, + 66, 67, 10, 8, 63, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 60, 62, + 6, 61, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 63, 2, 64, 4, 2, 2, 2, 2, 2, + 2, 64, 2, 65, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 68, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 69, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -497,7 +499,7 @@ static const yytype_uint8 yytranslate[] = 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 + 55, 56, 57, 58, 59 }; #if YYDEBUG @@ -509,71 +511,72 @@ static const yytype_uint16 yyprhs[] = 26, 31, 33, 36, 39, 47, 54, 60, 66, 72, 77, 82, 86, 90, 95, 102, 110, 118, 126, 133, 140, 144, 149, 156, 163, 168, 172, 178, 184, 192, - 199, 212, 220, 230, 233, 234, 237, 240, 241, 244, - 249, 252, 255, 258, 261, 266, 269, 271, 274, 278, - 280, 284, 288, 290, 292, 294, 299, 301, 303, 305, - 307, 309, 311, 313, 317, 319, 324, 326, 331, 333, - 335, 337, 339, 342, 344, 350, 355, 360, 365, 370, - 372, 374, 376, 378, 383, 385, 387, 389, 394, 396, - 398, 400, 405, 410, 416, 424, 425, 428, 431, 433, - 435, 437, 439, 441, 444, 447, 450, 454, 455, 458, - 460, 464, 468, 472, 476, 480, 485, 490, 494, 498 + 199, 212, 220, 230, 233, 236, 237, 240, 243, 244, + 247, 252, 255, 258, 261, 264, 269, 272, 274, 277, + 281, 283, 287, 291, 293, 295, 297, 302, 304, 306, + 308, 310, 312, 314, 316, 320, 322, 327, 329, 334, + 336, 338, 340, 342, 345, 347, 353, 358, 363, 368, + 373, 375, 377, 379, 381, 386, 388, 390, 392, 397, + 399, 401, 403, 408, 413, 419, 427, 428, 431, 434, + 436, 438, 440, 442, 444, 447, 450, 453, 457, 458, + 461, 463, 467, 471, 475, 479, 483, 488, 493, 497, + 501 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 70, 0, -1, -1, -1, 70, 71, 72, -1, -1, - 57, 59, 73, 72, -1, -1, 56, 59, 74, 72, - -1, 56, 60, 103, 61, -1, 58, 60, 103, 61, - -1, 61, -1, 75, 61, -1, 1, 61, -1, 13, - 76, 87, 62, 94, 62, 89, -1, 13, 76, 87, - 62, 94, 62, -1, 13, 76, 87, 62, 89, -1, - 14, 76, 87, 62, 89, -1, 15, 76, 82, 62, - 82, -1, 16, 76, 77, 78, -1, 16, 76, 77, - 83, -1, 36, 77, 84, -1, 17, 77, 78, -1, - 18, 76, 77, 82, -1, 19, 76, 87, 62, 94, - 77, -1, 20, 76, 85, 62, 63, 81, 64, -1, - 20, 76, 63, 81, 64, 62, 85, -1, 21, 76, - 89, 62, 84, 62, 89, -1, 21, 76, 89, 62, - 84, 77, -1, 21, 76, 77, 84, 62, 89, -1, - 22, 76, 77, -1, 23, 98, 62, 88, -1, 23, - 98, 62, 101, 62, 88, -1, 24, 98, 11, 101, - 62, 79, -1, 25, 76, 89, 77, -1, 29, 77, - 79, -1, 30, 76, 97, 62, 97, -1, 32, 76, - 96, 62, 97, -1, 32, 76, 96, 62, 46, 62, - 97, -1, 33, 76, 97, 62, 97, 77, -1, 31, - 76, 101, 62, 103, 62, 94, 62, 95, 62, 95, - 102, -1, 34, 76, 89, 62, 89, 62, 90, -1, - 35, 76, 89, 62, 89, 62, 89, 62, 94, -1, - 26, 77, -1, -1, 76, 51, -1, 76, 52, -1, - -1, 62, 77, -1, 101, 65, 41, 66, -1, 56, - 99, -1, 57, 99, -1, 67, 101, -1, 67, 86, - -1, 67, 10, 67, 86, -1, 67, 55, -1, 80, - -1, 67, 54, -1, 67, 9, 54, -1, 94, -1, - 94, 9, 94, -1, 94, 77, 81, -1, 89, -1, - 79, -1, 91, -1, 91, 65, 94, 66, -1, 49, - -1, 50, -1, 101, -1, 86, -1, 97, -1, 84, - -1, 98, -1, 65, 94, 66, -1, 84, -1, 101, - 65, 93, 66, -1, 98, -1, 98, 65, 93, 66, - -1, 85, -1, 89, -1, 88, -1, 91, -1, 67, - 101, -1, 94, -1, 65, 94, 62, 94, 66, -1, - 94, 6, 6, 92, -1, 94, 7, 7, 92, -1, - 94, 9, 7, 92, -1, 94, 53, 7, 92, -1, - 94, -1, 101, -1, 44, -1, 41, -1, 43, 65, - 103, 66, -1, 93, -1, 38, -1, 48, -1, 47, - 65, 103, 66, -1, 97, -1, 80, -1, 46, -1, - 45, 65, 101, 66, -1, 101, 65, 100, 66, -1, - 56, 99, 65, 100, 66, -1, 56, 6, 7, 99, - 65, 39, 66, -1, -1, 8, 101, -1, 9, 101, - -1, 39, -1, 38, -1, 40, -1, 37, -1, 58, - -1, 9, 101, -1, 8, 101, -1, 68, 101, -1, - 65, 103, 66, -1, -1, 62, 103, -1, 101, -1, - 103, 8, 103, -1, 103, 9, 103, -1, 103, 10, - 103, -1, 103, 11, 103, -1, 103, 12, 103, -1, - 103, 6, 6, 103, -1, 103, 7, 7, 103, -1, - 103, 5, 103, -1, 103, 4, 103, -1, 103, 3, - 103, -1 + 71, 0, -1, -1, -1, 71, 72, 73, -1, -1, + 58, 60, 74, 73, -1, -1, 57, 60, 75, 73, + -1, 57, 61, 104, 62, -1, 59, 61, 104, 62, + -1, 62, -1, 76, 62, -1, 1, 62, -1, 13, + 77, 88, 63, 95, 63, 90, -1, 13, 77, 88, + 63, 95, 63, -1, 13, 77, 88, 63, 90, -1, + 14, 77, 88, 63, 90, -1, 15, 77, 83, 63, + 83, -1, 16, 77, 78, 79, -1, 16, 77, 78, + 84, -1, 36, 78, 85, -1, 17, 78, 79, -1, + 18, 77, 78, 83, -1, 19, 77, 88, 63, 95, + 78, -1, 20, 77, 86, 63, 64, 82, 65, -1, + 20, 77, 64, 82, 65, 63, 86, -1, 21, 77, + 90, 63, 85, 63, 90, -1, 21, 77, 90, 63, + 85, 78, -1, 21, 77, 78, 85, 63, 90, -1, + 22, 77, 78, -1, 23, 99, 63, 89, -1, 23, + 99, 63, 102, 63, 89, -1, 24, 99, 11, 102, + 63, 80, -1, 25, 77, 90, 78, -1, 29, 78, + 80, -1, 30, 77, 98, 63, 98, -1, 32, 77, + 97, 63, 98, -1, 32, 77, 97, 63, 47, 63, + 98, -1, 33, 77, 98, 63, 98, 78, -1, 31, + 77, 102, 63, 104, 63, 95, 63, 96, 63, 96, + 103, -1, 34, 77, 90, 63, 90, 63, 91, -1, + 35, 77, 90, 63, 90, 63, 90, 63, 95, -1, + 37, 87, -1, 26, 78, -1, -1, 77, 52, -1, + 77, 53, -1, -1, 63, 78, -1, 102, 66, 42, + 67, -1, 57, 100, -1, 58, 100, -1, 68, 102, + -1, 68, 87, -1, 68, 10, 68, 87, -1, 68, + 56, -1, 81, -1, 68, 55, -1, 68, 9, 55, + -1, 95, -1, 95, 9, 95, -1, 95, 78, 82, + -1, 90, -1, 80, -1, 92, -1, 92, 66, 95, + 67, -1, 50, -1, 51, -1, 102, -1, 87, -1, + 98, -1, 85, -1, 99, -1, 66, 95, 67, -1, + 85, -1, 102, 66, 94, 67, -1, 99, -1, 99, + 66, 94, 67, -1, 86, -1, 90, -1, 89, -1, + 92, -1, 68, 102, -1, 95, -1, 66, 95, 63, + 95, 67, -1, 95, 6, 6, 93, -1, 95, 7, + 7, 93, -1, 95, 9, 7, 93, -1, 95, 54, + 7, 93, -1, 95, -1, 102, -1, 45, -1, 42, + -1, 44, 66, 104, 67, -1, 94, -1, 39, -1, + 49, -1, 48, 66, 104, 67, -1, 98, -1, 81, + -1, 47, -1, 46, 66, 102, 67, -1, 102, 66, + 101, 67, -1, 57, 100, 66, 101, 67, -1, 57, + 6, 7, 100, 66, 40, 67, -1, -1, 8, 102, + -1, 9, 102, -1, 40, -1, 39, -1, 41, -1, + 38, -1, 59, -1, 9, 102, -1, 8, 102, -1, + 69, 102, -1, 66, 104, 67, -1, -1, 63, 104, + -1, 102, -1, 104, 8, 104, -1, 104, 9, 104, + -1, 104, 10, 104, -1, 104, 11, 104, -1, 104, + 12, 104, -1, 104, 6, 6, 104, -1, 104, 7, + 7, 104, -1, 104, 5, 104, -1, 104, 4, 104, + -1, 104, 3, 104, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -583,15 +586,16 @@ static const yytype_uint16 yyrline[] = 93, 99, 100, 101, 107, 111, 115, 122, 129, 136, 140, 147, 154, 161, 168, 175, 184, 196, 200, 204, 211, 218, 222, 229, 236, 243, 250, 254, 258, 262, - 269, 291, 298, 307, 313, 316, 320, 325, 326, 329, - 335, 344, 352, 358, 363, 368, 374, 377, 383, 391, - 395, 404, 410, 411, 412, 413, 418, 424, 430, 436, - 437, 440, 441, 449, 458, 459, 468, 469, 475, 478, - 479, 480, 482, 490, 498, 507, 513, 519, 525, 533, - 539, 547, 548, 552, 560, 561, 567, 568, 576, 577, - 580, 586, 594, 602, 610, 620, 623, 627, 633, 634, - 635, 638, 639, 643, 647, 651, 655, 661, 664, 670, - 671, 675, 679, 683, 687, 691, 695, 699, 703, 707 + 269, 291, 299, 308, 315, 321, 324, 328, 333, 334, + 337, 343, 352, 360, 366, 371, 376, 382, 385, 391, + 399, 403, 412, 418, 419, 420, 421, 426, 432, 438, + 444, 445, 448, 449, 457, 466, 467, 476, 477, 483, + 486, 487, 488, 490, 498, 506, 515, 521, 527, 533, + 541, 547, 555, 556, 560, 568, 569, 575, 576, 584, + 585, 588, 594, 602, 610, 618, 628, 631, 635, 641, + 642, 643, 646, 647, 651, 655, 659, 663, 669, 672, + 678, 679, 683, 687, 691, 695, 699, 703, 707, 711, + 715 }; #endif @@ -604,13 +608,13 @@ static const char *const yytname[] = "'-'", "'*'", "'/'", "'%'", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPE5", "LTYPE6", "LTYPE7", "LTYPE8", "LTYPE9", "LTYPEA", "LTYPEB", "LTYPEC", "LTYPED", "LTYPEE", "LTYPEF", "LTYPEG", "LTYPEH", "LTYPEI", - "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LCONST", - "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF", "LFREG", "LC", - "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST", "LSCONST", - "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['", "']'", "'('", - "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", "$@2", "$@3", - "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist", "gen", - "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg", + "LTYPEJ", "LTYPEK", "LTYPEL", "LTYPEM", "LTYPEN", "LTYPEBX", "LTYPEPLD", + "LCONST", "LSP", "LSB", "LFP", "LPC", "LTYPEX", "LR", "LREG", "LF", + "LFREG", "LC", "LCREG", "LPSR", "LFCR", "LCOND", "LS", "LAT", "LFCONST", + "LSCONST", "LNAME", "LLAB", "LVAR", "':'", "'='", "';'", "','", "'['", + "']'", "'('", "')'", "'$'", "'~'", "$accept", "prog", "$@1", "line", + "$@2", "$@3", "inst", "cond", "comma", "rel", "ximm", "fcon", "reglist", + "gen", "nireg", "ireg", "ioreg", "oreg", "imsr", "imm", "reg", "regreg", "shift", "rcon", "sreg", "spreg", "creg", "frcon", "freg", "name", "offset", "pointer", "con", "oexpr", "expr", 0 }; @@ -626,27 +630,28 @@ static const yytype_uint16 yytoknum[] = 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, - 295, 296, 297, 298, 299, 300, 301, 302, 303, 58, - 61, 59, 44, 91, 93, 40, 41, 36, 126 + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 58, 61, 59, 44, 91, 93, 40, 41, 36, 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 69, 70, 71, 70, 73, 72, 74, 72, 72, - 72, 72, 72, 72, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, - 75, 75, 75, 75, 76, 76, 76, 77, 77, 78, - 78, 78, 79, 79, 79, 79, 79, 80, 80, 81, - 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, - 82, 83, 83, 84, 85, 85, 86, 86, 86, 87, - 87, 87, 88, 89, 90, 91, 91, 91, 91, 92, - 92, 93, 93, 93, 94, 94, 95, 95, 96, 96, - 97, 97, 98, 98, 98, 99, 99, 99, 100, 100, - 100, 101, 101, 101, 101, 101, 101, 102, 102, 103, - 103, 103, 103, 103, 103, 103, 103, 103, 103, 103 + 0, 70, 71, 72, 71, 74, 73, 75, 73, 73, + 73, 73, 73, 73, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 77, 77, 77, 78, 78, + 79, 79, 79, 80, 80, 80, 80, 80, 81, 81, + 82, 82, 82, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 84, 84, 85, 86, 86, 87, 87, 87, + 88, 88, 88, 89, 90, 91, 92, 92, 92, 92, + 93, 93, 94, 94, 94, 95, 95, 96, 96, 97, + 97, 98, 98, 99, 99, 99, 100, 100, 100, 101, + 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 104 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -656,15 +661,16 @@ static const yytype_uint8 yyr2[] = 4, 1, 2, 2, 7, 6, 5, 5, 5, 4, 4, 3, 3, 4, 6, 7, 7, 7, 6, 6, 3, 4, 6, 6, 4, 3, 5, 5, 7, 6, - 12, 7, 9, 2, 0, 2, 2, 0, 2, 4, - 2, 2, 2, 2, 4, 2, 1, 2, 3, 1, - 3, 3, 1, 1, 1, 4, 1, 1, 1, 1, - 1, 1, 1, 3, 1, 4, 1, 4, 1, 1, - 1, 1, 2, 1, 5, 4, 4, 4, 4, 1, - 1, 1, 1, 4, 1, 1, 1, 4, 1, 1, - 1, 4, 4, 5, 7, 0, 2, 2, 1, 1, - 1, 1, 1, 2, 2, 2, 3, 0, 2, 1, - 3, 3, 3, 3, 3, 4, 4, 3, 3, 3 + 12, 7, 9, 2, 2, 0, 2, 2, 0, 2, + 4, 2, 2, 2, 2, 4, 2, 1, 2, 3, + 1, 3, 3, 1, 1, 1, 4, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 4, 1, 4, 1, + 1, 1, 1, 2, 1, 5, 4, 4, 4, 4, + 1, 1, 1, 1, 4, 1, 1, 1, 4, 1, + 1, 1, 4, 4, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 0, 2, + 1, 3, 3, 3, 3, 3, 4, 4, 3, 3, + 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -672,279 +678,271 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 44, 44, 44, 44, 47, - 44, 44, 44, 44, 44, 0, 0, 44, 47, 47, - 44, 44, 44, 44, 44, 44, 47, 0, 0, 0, - 11, 4, 0, 13, 0, 0, 0, 47, 47, 0, - 47, 0, 0, 47, 47, 0, 0, 111, 105, 112, - 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, - 0, 0, 0, 0, 0, 7, 0, 5, 0, 12, - 95, 92, 0, 91, 45, 46, 0, 0, 80, 79, - 81, 94, 83, 0, 0, 100, 66, 67, 0, 0, - 63, 56, 0, 74, 78, 69, 62, 64, 70, 76, - 68, 0, 48, 105, 105, 22, 0, 0, 0, 0, - 0, 0, 0, 0, 83, 30, 114, 113, 0, 0, - 0, 0, 119, 0, 115, 0, 0, 0, 47, 35, - 0, 0, 0, 99, 0, 98, 0, 0, 0, 0, - 21, 0, 0, 0, 0, 0, 82, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 57, 55, 53, - 52, 0, 0, 0, 0, 105, 19, 20, 71, 72, - 0, 50, 51, 0, 23, 0, 0, 47, 0, 0, - 0, 0, 105, 106, 107, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 116, 31, 0, 109, - 108, 110, 0, 0, 34, 0, 0, 0, 0, 0, - 0, 0, 8, 9, 6, 10, 0, 16, 83, 0, - 0, 0, 0, 17, 0, 73, 58, 0, 18, 0, - 0, 0, 50, 0, 0, 47, 0, 0, 0, 0, - 0, 47, 0, 0, 129, 128, 127, 0, 0, 120, - 121, 122, 123, 124, 0, 102, 0, 36, 0, 100, - 37, 47, 0, 0, 93, 15, 85, 89, 90, 86, - 87, 88, 101, 54, 0, 65, 77, 75, 49, 24, - 0, 60, 61, 0, 29, 47, 28, 0, 103, 125, - 126, 32, 33, 0, 0, 39, 0, 0, 14, 26, - 25, 27, 0, 0, 38, 0, 41, 0, 104, 0, - 0, 0, 0, 96, 0, 0, 42, 0, 0, 0, - 0, 117, 84, 97, 0, 40, 118 + 2, 3, 1, 0, 0, 45, 45, 45, 45, 48, + 45, 45, 45, 45, 45, 0, 0, 45, 48, 48, + 45, 45, 45, 45, 45, 45, 48, 0, 0, 0, + 0, 11, 4, 0, 13, 0, 0, 0, 48, 48, + 0, 48, 0, 0, 48, 48, 0, 0, 112, 106, + 113, 0, 0, 0, 0, 0, 0, 44, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 75, 79, 43, + 77, 0, 7, 0, 5, 0, 12, 96, 93, 0, + 92, 46, 47, 0, 0, 81, 80, 82, 95, 84, + 0, 0, 101, 67, 68, 0, 64, 57, 0, 70, + 63, 65, 71, 69, 0, 49, 106, 106, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 84, 30, 115, + 114, 0, 0, 0, 0, 120, 0, 116, 0, 0, + 0, 48, 35, 0, 0, 0, 100, 0, 99, 0, + 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, + 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 58, 56, 54, 53, 0, 0, 106, 19, + 20, 72, 73, 0, 51, 52, 0, 23, 0, 0, + 48, 0, 0, 0, 0, 106, 107, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 31, 0, 110, 109, 111, 0, 0, 34, 0, 0, + 0, 0, 0, 0, 0, 74, 0, 0, 8, 9, + 6, 10, 0, 16, 84, 0, 0, 0, 0, 17, + 0, 59, 0, 18, 0, 51, 0, 0, 48, 0, + 0, 0, 0, 0, 48, 0, 0, 130, 129, 128, + 0, 0, 121, 122, 123, 124, 125, 0, 103, 0, + 36, 0, 101, 37, 48, 0, 0, 78, 76, 94, + 15, 86, 90, 91, 87, 88, 89, 102, 55, 66, + 50, 24, 0, 61, 62, 0, 29, 48, 28, 0, + 104, 126, 127, 32, 33, 0, 0, 39, 0, 0, + 14, 26, 25, 27, 0, 0, 38, 0, 41, 0, + 105, 0, 0, 0, 0, 97, 0, 0, 42, 0, + 0, 0, 0, 118, 85, 98, 0, 40, 119 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 31, 143, 141, 32, 34, 102, 105, - 90, 91, 176, 92, 167, 93, 94, 95, 77, 78, - 79, 306, 80, 266, 81, 114, 314, 134, 98, 99, - 121, 202, 122, 325, 123 + -1, 1, 3, 32, 149, 147, 33, 35, 105, 108, + 96, 97, 179, 98, 170, 67, 68, 99, 84, 85, + 86, 308, 87, 271, 88, 117, 316, 137, 102, 70, + 124, 205, 125, 327, 126 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -147 +#define YYPACT_NINF -114 static const yytype_int16 yypact[] = { - -147, 19, -147, 305, -37, -147, -147, -147, -147, -40, - -147, -147, -147, -147, -147, 448, 448, -147, -40, -40, - -147, -147, -147, -147, -147, -147, -40, -8, -22, -21, - -147, -147, -20, -147, 102, 102, 334, 108, -40, 416, - 108, 102, 397, 48, 108, 482, 482, -147, 95, -147, - 482, 482, -19, 18, 34, 223, -147, 5, 174, 424, - 132, 174, 223, 223, 20, -147, 482, -147, 482, -147, - -147, -147, 30, -147, -147, -147, 482, 35, -147, -147, - -147, -147, 49, 76, 84, -147, -147, -147, 205, 339, - -147, -147, 93, -147, -147, -147, -147, 110, -147, 124, - 125, 442, -147, 98, 98, -147, 128, 373, 134, 36, - 144, 147, 20, 153, -147, -147, -147, -147, 60, 482, - 482, 175, -147, 114, -147, 249, 21, 482, -40, -147, - 182, 183, 12, -147, 185, -147, 188, 189, 198, 36, - -147, 305, 562, 305, 572, 482, -147, 36, 256, 258, - 261, 269, 36, 482, 212, 460, 216, -147, -147, -147, - 125, 373, 36, 138, 586, 95, -147, -147, -147, -147, - 217, -147, -147, 247, -147, 36, 226, 7, 229, 138, - 227, 20, 98, -147, -147, 21, 482, 482, 482, 287, - 301, 482, 482, 482, 482, 482, -147, -147, 248, -147, - -147, -147, 243, 250, -147, 66, 482, 257, 106, 66, - 36, 36, -147, -147, -147, -147, 225, -147, 251, 205, - 205, 205, 205, -147, 266, -147, -147, 478, -147, 267, - 278, 279, 175, 215, 280, -40, 253, 36, 36, 36, - 36, 297, 295, 298, 601, 345, 609, 482, 482, 190, - 190, -147, -147, -147, 300, -147, 5, -147, 552, 303, - -147, -40, 306, 307, -147, 36, -147, -147, -147, -147, - -147, -147, -147, -147, 125, -147, -147, -147, -147, -147, - 486, -147, -147, 309, -147, 130, -147, 331, -147, 121, - 121, -147, -147, 36, 66, -147, 322, 36, -147, -147, - -147, -147, 308, 326, -147, 36, -147, 327, -147, 119, - 329, 36, 333, -147, 338, 36, -147, 482, 119, 330, - 292, 341, -147, -147, 482, -147, 631 + -114, 19, -114, 311, -20, -114, -114, -114, -114, -36, + -114, -114, -114, -114, -114, 413, 413, -114, -36, -36, + -114, -114, -114, -114, -114, -114, -36, 427, -22, -9, + -7, -114, -114, 11, -114, 480, 480, 341, 45, -36, + 229, 45, 480, 210, 436, 45, 435, 435, -114, 152, + -114, 435, 435, 25, 15, 79, 517, -114, 24, 134, + 393, 197, 134, 517, 517, 28, 71, -114, -114, -114, + 35, 58, -114, 435, -114, 435, -114, -114, -114, 69, + -114, -114, -114, 435, 51, -114, -114, -114, -114, 46, + 57, 75, -114, -114, -114, 119, -114, -114, 82, -114, + -114, 88, -114, 58, 368, -114, 159, 159, -114, 113, + 373, 114, 411, 120, 123, 28, 145, -114, -114, -114, + -114, 193, 435, 435, 173, -114, 54, -114, 395, 111, + 435, -36, -114, 178, 182, 21, -114, 198, -114, 201, + 207, 212, 411, -114, 206, 168, 558, 311, 310, 311, + 506, 435, -114, 411, 271, 274, 285, 286, 411, 435, + 87, 228, -114, -114, -114, 58, 373, 411, 152, -114, + -114, -114, -114, 231, -114, -114, 257, -114, 411, 235, + 5, 259, 168, 275, 28, 159, -114, -114, 111, 435, + 435, 435, 333, 344, 435, 435, 435, 435, 435, -114, + -114, 289, -114, -114, -114, 300, 308, -114, 151, 435, + 319, 176, 151, 411, 411, -114, 317, 322, -114, -114, + -114, -114, 224, -114, 312, 71, 71, 71, 71, -114, + 323, -114, 427, -114, 328, 173, 64, 329, -36, 315, + 411, 411, 411, 411, 334, 339, 332, 577, 247, 584, + 435, 435, 273, 273, -114, -114, -114, 340, -114, 24, + -114, 350, 351, -114, -36, 353, 365, -114, -114, -114, + 411, -114, -114, -114, -114, -114, -114, -114, -114, -114, + -114, -114, 439, -114, -114, 364, -114, 157, -114, 398, + -114, 518, 518, -114, -114, 411, 151, -114, 374, 411, + -114, -114, -114, -114, 382, 394, -114, 411, -114, 397, + -114, 155, 403, 411, 392, -114, 404, 411, -114, 435, + 155, 401, 299, 406, -114, -114, 435, -114, 568 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -147, -147, -147, -114, -147, -147, -147, 579, 44, 311, - -49, 348, 0, -97, -147, -44, -39, -80, -9, -119, - -13, -147, -25, 2, -146, -34, 91, -147, -14, -3, - -89, 228, -11, -147, -30 + -114, -114, -114, -78, -114, -114, -114, 529, 2, 367, + -50, 415, 48, -88, -114, -48, -40, -21, 47, -113, + -19, -114, -28, 137, -59, -35, 154, -114, -49, 18, + -83, 295, -11, -114, -25 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -60 +#define YYTABLE_NINF -61 static const yytype_int16 yytable[] = { - 82, 82, 82, 110, 53, 53, 197, 82, 129, 159, - 174, 97, 52, 54, 171, 172, 237, 230, 231, 2, - 140, 207, 38, 96, 33, 100, 83, 212, 106, 214, - 113, 111, 108, 231, 116, 117, 142, 67, 144, 68, - 124, 69, 128, 125, 130, 127, 135, 136, 131, 137, - 138, 65, 66, 39, 154, 148, 149, 168, 150, 199, - 200, 201, 56, 57, 228, 146, 157, 182, 180, 38, - 64, -59, 89, 82, 70, 177, 232, 71, 160, 72, - 73, 101, 97, 126, 107, 139, 70, 112, 115, 71, - 170, 72, 73, 242, 96, 145, 100, 147, 169, 74, - 75, 118, 151, 119, 120, 154, 119, 120, 183, 184, - 38, 84, 85, 218, 198, 216, 203, 186, 187, 188, - 189, 190, 191, 192, 193, 194, 195, 82, 229, 191, - 192, 193, 194, 195, 217, 291, 97, 241, 152, 223, - 70, 235, 224, 71, 117, 72, 73, 273, 96, 153, - 100, 84, 259, 74, 75, 161, 244, 245, 246, 74, - 75, 249, 250, 251, 252, 253, 312, 313, 70, 76, - 38, 71, 204, 72, 73, 162, 258, 84, 85, 71, - 196, 72, 73, 74, 75, 267, 267, 267, 267, 163, - 164, 257, 38, 173, 260, 261, 175, 262, 263, 132, - 193, 194, 195, 281, 177, 177, 178, 292, 268, 268, - 268, 268, 179, 45, 46, 181, 274, 289, 290, 84, - 85, 238, 269, 270, 271, 74, 75, 284, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 282, 283, - 185, 299, 47, 70, 205, 206, 71, 208, 72, 73, - 209, 210, 298, 199, 200, 201, 234, 45, 46, 303, - 211, 70, 219, 49, 71, 220, 72, 73, 221, 111, - 50, 310, 301, 51, 74, 75, 222, 316, 225, 279, - 304, 319, 233, 227, 307, 286, 47, 320, 234, 240, - 236, 264, 239, 247, 326, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 295, 4, 49, 248, 255, - 254, 226, 256, 265, 50, 280, 76, 51, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 272, 275, 19, 20, 21, 22, 23, 24, - 25, 26, 45, 46, 276, 277, 278, 45, 155, 156, - 188, 189, 190, 191, 192, 193, 194, 195, 323, 285, - 287, 27, 28, 29, 288, 294, 30, 76, 296, 297, - 302, 47, 70, 300, 308, 71, 47, 72, 73, 84, - 85, 45, 46, 86, 87, 74, 75, 305, 309, 311, - 48, 315, 49, 157, 158, 48, 322, 49, 317, 88, - 318, 89, 51, 324, 88, 45, 46, 51, 133, 321, - 47, 70, 166, 243, 71, 0, 72, 73, 84, 85, - 0, 0, 86, 87, 45, 46, 0, 0, 0, 48, - 0, 49, 45, 46, 47, 0, 0, 0, 88, 0, - 89, 51, 0, 0, 0, 0, 0, 0, 74, 75, - 45, 46, 0, 47, 0, 49, 45, 46, 0, 0, - 109, 47, 88, 0, 0, 51, 0, 0, 45, 46, - 0, 0, 103, 104, 49, 74, 75, 0, 0, 47, - 0, 50, 49, 0, 51, 47, 45, 46, 0, 50, - 45, 46, 51, 0, 45, 46, 0, 47, 165, 104, - 49, 0, 0, 0, 48, 0, 49, 88, 0, 0, - 51, 0, 0, 50, 226, 47, 51, 0, 49, 47, - 0, 0, 0, 47, 0, 50, 0, 0, 51, 0, - 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, - 49, 0, 0, 88, 49, 0, 51, 50, 0, 0, - 51, 88, 0, 0, 51, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 35, 36, 37, 0, 40, - 41, 42, 43, 44, 0, 0, 55, 0, 0, 58, - 59, 60, 61, 62, 63, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 293, 189, 190, 191, 192, 193, - 194, 195, 0, 213, 199, 200, 201, 71, 0, 72, - 73, 0, 0, 215, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195 + 89, 89, 89, 113, 54, 54, 69, 89, 132, 101, + 133, 40, 138, 139, 240, 200, 71, 143, 100, 2, + 57, 58, 177, 174, 175, 116, 103, 39, 65, 109, + 210, 144, 114, 53, 55, 119, 120, 131, 72, 73, + 104, 127, 34, 110, 140, 141, 115, 118, 148, 134, + 150, 74, 154, 155, 75, 156, 171, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 183, 39, 218, + -60, 220, 152, 76, 164, 89, 162, 180, 233, 46, + 47, 129, 101, 90, 165, 235, 216, 217, 128, 111, + 130, 100, 95, 173, 142, 46, 47, 81, 82, 103, + 157, 145, 245, 202, 203, 204, 237, 144, 39, 48, + 77, 186, 187, 78, 153, 79, 80, 201, 224, 206, + 158, 199, 172, 217, 146, 48, 222, 46, 160, 161, + 50, 89, 234, 207, 223, 151, 244, 51, 101, 229, + 52, 159, 231, 238, 293, 166, 50, 100, 230, 120, + 202, 203, 204, 51, 167, 103, 52, 48, 121, 260, + 122, 123, 263, 264, 247, 248, 249, 122, 123, 252, + 253, 254, 255, 256, 162, 163, 49, 178, 50, 176, + 91, 92, 241, 181, 261, 66, 81, 82, 52, 182, + 272, 272, 272, 272, 265, 266, 77, 91, 92, 78, + 185, 79, 80, 314, 315, 283, 180, 180, 184, 294, + 78, 278, 79, 80, 273, 273, 273, 273, 46, 47, + 39, 71, 91, 262, 286, 291, 292, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 46, 47, 188, + 281, 208, 301, 91, 92, 209, 288, 306, 48, 81, + 82, 300, 191, 192, 193, 194, 195, 196, 197, 198, + 305, 211, 81, 82, 212, 135, 297, 48, 303, 50, + 213, 114, 312, 215, 112, 214, 66, 225, 318, 52, + 309, 226, 321, 196, 197, 198, 106, 107, 50, 284, + 285, 269, 227, 228, 322, 51, 232, 236, 52, 237, + 239, 328, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 4, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 242, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 243, 250, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 46, + 47, 251, 257, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 274, 275, 276, 325, 258, 28, 29, + 30, 259, 219, 31, 231, 270, 46, 47, 282, 48, + 77, 46, 47, 78, 267, 79, 80, 91, 92, 268, + 277, 93, 94, 81, 82, 279, 280, 287, 49, 290, + 50, 46, 47, 46, 47, 289, 48, 66, 83, 95, + 52, 48, 77, 295, 296, 78, 298, 79, 80, 91, + 92, 46, 47, 93, 94, 168, 107, 50, 299, 302, + 49, 48, 50, 48, 66, 46, 47, 52, 304, 66, + 307, 95, 52, 46, 47, 81, 82, 46, 47, 310, + 77, 48, 50, 78, 50, 79, 80, 311, 319, 51, + 313, 51, 52, 83, 52, 48, 317, 320, 324, 326, + 49, 169, 50, 48, 323, 77, 136, 48, 78, 51, + 79, 80, 52, 246, 49, 0, 50, 0, 81, 82, + 0, 0, 0, 66, 50, 0, 52, 0, 50, 39, + 0, 51, 0, 0, 52, 66, 0, 0, 52, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 77, + 0, 0, 78, 0, 79, 80, 194, 195, 196, 197, + 198, 0, 81, 82, 0, 36, 37, 38, 0, 41, + 42, 43, 44, 45, 0, 0, 56, 0, 83, 59, + 60, 61, 62, 63, 64, 0, 77, 0, 0, 78, + 0, 79, 80, 0, 0, 0, 0, 0, 221, 81, + 82, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 192, 193, 194, 195, 196, 197, 198, 202, 203, 204, + 78, 0, 79, 80 }; static const yytype_int16 yycheck[] = { - 34, 35, 36, 42, 15, 16, 125, 41, 57, 89, - 107, 36, 15, 16, 103, 104, 9, 163, 164, 0, - 64, 9, 62, 36, 61, 36, 35, 141, 39, 143, - 43, 42, 41, 179, 45, 46, 66, 59, 68, 60, - 51, 61, 55, 62, 58, 11, 60, 61, 59, 62, - 63, 59, 60, 9, 88, 6, 7, 101, 9, 38, - 39, 40, 18, 19, 161, 76, 54, 7, 112, 62, - 26, 64, 67, 107, 38, 109, 165, 41, 89, 43, - 44, 37, 107, 65, 40, 65, 38, 43, 44, 41, - 101, 43, 44, 182, 107, 65, 107, 62, 101, 51, - 52, 6, 53, 8, 9, 139, 8, 9, 119, 120, - 62, 45, 46, 147, 125, 145, 127, 3, 4, 5, - 6, 7, 8, 9, 10, 11, 12, 161, 162, 8, - 9, 10, 11, 12, 147, 254, 161, 181, 62, 152, - 38, 175, 153, 41, 155, 43, 44, 227, 161, 65, - 161, 45, 46, 51, 52, 62, 186, 187, 188, 51, - 52, 191, 192, 193, 194, 195, 47, 48, 38, 67, - 62, 41, 128, 43, 44, 65, 206, 45, 46, 41, - 66, 43, 44, 51, 52, 219, 220, 221, 222, 65, - 65, 205, 62, 65, 208, 209, 62, 210, 211, 67, - 10, 11, 12, 237, 238, 239, 62, 256, 219, 220, - 221, 222, 65, 8, 9, 62, 227, 247, 248, 45, - 46, 177, 220, 221, 222, 51, 52, 240, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 238, 239, - 65, 280, 37, 38, 62, 62, 41, 62, 43, 44, - 62, 62, 265, 38, 39, 40, 41, 8, 9, 293, - 62, 38, 6, 58, 41, 7, 43, 44, 7, 280, - 65, 305, 285, 68, 51, 52, 7, 311, 66, 235, - 294, 315, 65, 67, 297, 241, 37, 317, 41, 62, - 64, 66, 63, 6, 324, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 261, 1, 58, 7, 66, - 62, 54, 62, 62, 65, 62, 67, 68, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 66, 66, 29, 30, 31, 32, 33, 34, - 35, 36, 8, 9, 66, 66, 66, 8, 9, 10, - 5, 6, 7, 8, 9, 10, 11, 12, 66, 62, - 65, 56, 57, 58, 66, 62, 61, 67, 62, 62, - 39, 37, 38, 64, 66, 41, 37, 43, 44, 45, - 46, 8, 9, 49, 50, 51, 52, 65, 62, 62, - 56, 62, 58, 54, 55, 56, 66, 58, 65, 65, - 62, 67, 68, 62, 65, 8, 9, 68, 60, 318, - 37, 38, 101, 185, 41, -1, 43, 44, 45, 46, - -1, -1, 49, 50, 8, 9, -1, -1, -1, 56, - -1, 58, 8, 9, 37, -1, -1, -1, 65, -1, - 67, 68, -1, -1, -1, -1, -1, -1, 51, 52, - 8, 9, -1, 37, -1, 58, 8, 9, -1, -1, - 63, 37, 65, -1, -1, 68, -1, -1, 8, 9, - -1, -1, 56, 57, 58, 51, 52, -1, -1, 37, - -1, 65, 58, -1, 68, 37, 8, 9, -1, 65, - 8, 9, 68, -1, 8, 9, -1, 37, 56, 57, - 58, -1, -1, -1, 56, -1, 58, 65, -1, -1, - 68, -1, -1, 65, 54, 37, 68, -1, 58, 37, - -1, -1, -1, 37, -1, 65, -1, -1, 68, -1, - -1, -1, -1, -1, 56, -1, 58, -1, -1, -1, - 58, -1, -1, 65, 58, -1, 68, 65, -1, -1, - 68, 65, -1, -1, 68, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 6, 7, 8, -1, 10, - 11, 12, 13, 14, -1, -1, 17, -1, -1, 20, - 21, 22, 23, 24, 25, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 62, 6, 7, 8, 9, 10, - 11, 12, -1, 61, 38, 39, 40, 41, -1, 43, - 44, -1, -1, 61, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12 + 35, 36, 37, 43, 15, 16, 27, 42, 58, 37, + 59, 9, 61, 62, 9, 128, 27, 65, 37, 0, + 18, 19, 110, 106, 107, 44, 37, 63, 26, 40, + 9, 66, 43, 15, 16, 46, 47, 56, 60, 61, + 38, 52, 62, 41, 63, 64, 44, 45, 73, 60, + 75, 60, 6, 7, 61, 9, 104, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 115, 63, 147, + 65, 149, 83, 62, 95, 110, 55, 112, 166, 8, + 9, 66, 110, 36, 95, 168, 145, 146, 63, 42, + 11, 110, 68, 104, 66, 8, 9, 52, 53, 110, + 54, 66, 185, 39, 40, 41, 42, 142, 63, 38, + 39, 122, 123, 42, 63, 44, 45, 128, 153, 130, + 63, 67, 104, 182, 66, 38, 151, 8, 9, 10, + 59, 166, 167, 131, 153, 66, 184, 66, 166, 158, + 69, 66, 55, 178, 257, 63, 59, 166, 159, 160, + 39, 40, 41, 66, 66, 166, 69, 38, 6, 208, + 8, 9, 211, 212, 189, 190, 191, 8, 9, 194, + 195, 196, 197, 198, 55, 56, 57, 63, 59, 66, + 46, 47, 180, 63, 209, 66, 52, 53, 69, 66, + 225, 226, 227, 228, 213, 214, 39, 46, 47, 42, + 7, 44, 45, 48, 49, 240, 241, 242, 63, 259, + 42, 232, 44, 45, 225, 226, 227, 228, 8, 9, + 63, 232, 46, 47, 243, 250, 251, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 8, 9, 66, + 238, 63, 282, 46, 47, 63, 244, 296, 38, 52, + 53, 270, 5, 6, 7, 8, 9, 10, 11, 12, + 295, 63, 52, 53, 63, 68, 264, 38, 287, 59, + 63, 282, 307, 67, 64, 63, 66, 6, 313, 69, + 299, 7, 317, 10, 11, 12, 57, 58, 59, 241, + 242, 67, 7, 7, 319, 66, 68, 66, 69, 42, + 65, 326, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 1, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 64, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 63, 6, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 8, + 9, 7, 63, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 226, 227, 228, 67, 67, 57, 58, + 59, 63, 62, 62, 55, 63, 8, 9, 63, 38, + 39, 8, 9, 42, 67, 44, 45, 46, 47, 67, + 67, 50, 51, 52, 53, 67, 67, 63, 57, 67, + 59, 8, 9, 8, 9, 66, 38, 66, 68, 68, + 69, 38, 39, 63, 63, 42, 63, 44, 45, 46, + 47, 8, 9, 50, 51, 57, 58, 59, 63, 65, + 57, 38, 59, 38, 66, 8, 9, 69, 40, 66, + 66, 68, 69, 8, 9, 52, 53, 8, 9, 67, + 39, 38, 59, 42, 59, 44, 45, 63, 66, 66, + 63, 66, 69, 68, 69, 38, 63, 63, 67, 63, + 57, 104, 59, 38, 320, 39, 61, 38, 42, 66, + 44, 45, 69, 188, 57, -1, 59, -1, 52, 53, + -1, -1, -1, 66, 59, -1, 69, -1, 59, 63, + -1, 66, -1, -1, 69, 66, -1, -1, 69, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 39, + -1, -1, 42, -1, 44, 45, 8, 9, 10, 11, + 12, -1, 52, 53, -1, 6, 7, 8, -1, 10, + 11, 12, 13, 14, -1, -1, 17, -1, 68, 20, + 21, 22, 23, 24, 25, -1, 39, -1, -1, 42, + -1, 44, 45, -1, -1, -1, -1, -1, 62, 52, + 53, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 6, 7, 8, 9, 10, 11, 12, 39, 40, 41, + 42, -1, 44, 45 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 70, 0, 71, 1, 13, 14, 15, 16, 17, + 0, 71, 0, 72, 1, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 29, - 30, 31, 32, 33, 34, 35, 36, 56, 57, 58, - 61, 72, 75, 61, 76, 76, 76, 76, 62, 77, - 76, 76, 76, 76, 76, 8, 9, 37, 56, 58, - 65, 68, 98, 101, 98, 76, 77, 77, 76, 76, - 76, 76, 76, 76, 77, 59, 60, 59, 60, 61, - 38, 41, 43, 44, 51, 52, 67, 87, 88, 89, - 91, 93, 94, 87, 45, 46, 49, 50, 65, 67, - 79, 80, 82, 84, 85, 86, 89, 91, 97, 98, - 101, 77, 77, 56, 57, 78, 101, 77, 87, 63, - 85, 101, 77, 89, 94, 77, 101, 101, 6, 8, - 9, 99, 101, 103, 101, 62, 65, 11, 89, 79, - 97, 101, 67, 80, 96, 97, 97, 89, 89, 65, - 84, 74, 103, 73, 103, 65, 101, 62, 6, 7, - 9, 53, 62, 65, 94, 9, 10, 54, 55, 86, - 101, 62, 65, 65, 65, 56, 78, 83, 84, 98, - 101, 99, 99, 65, 82, 62, 81, 94, 62, 65, - 84, 62, 7, 101, 101, 65, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 66, 88, 101, 38, - 39, 40, 100, 101, 77, 62, 62, 9, 62, 62, - 62, 62, 72, 61, 72, 61, 103, 89, 94, 6, - 7, 7, 7, 89, 101, 66, 54, 67, 82, 94, - 93, 93, 99, 65, 41, 94, 64, 9, 77, 63, - 62, 84, 99, 100, 103, 103, 103, 6, 7, 103, - 103, 103, 103, 103, 62, 66, 62, 97, 103, 46, - 97, 97, 89, 89, 66, 62, 92, 94, 101, 92, - 92, 92, 66, 86, 101, 66, 66, 66, 66, 77, - 62, 94, 81, 81, 89, 62, 77, 65, 66, 103, - 103, 88, 79, 62, 62, 77, 62, 62, 89, 85, - 64, 89, 39, 94, 97, 65, 90, 89, 66, 62, - 94, 62, 47, 48, 95, 62, 94, 65, 62, 94, - 103, 95, 66, 66, 62, 102, 103 + 30, 31, 32, 33, 34, 35, 36, 37, 57, 58, + 59, 62, 73, 76, 62, 77, 77, 77, 77, 63, + 78, 77, 77, 77, 77, 77, 8, 9, 38, 57, + 59, 66, 69, 99, 102, 99, 77, 78, 78, 77, + 77, 77, 77, 77, 77, 78, 66, 85, 86, 87, + 99, 102, 60, 61, 60, 61, 62, 39, 42, 44, + 45, 52, 53, 68, 88, 89, 90, 92, 94, 95, + 88, 46, 47, 50, 51, 68, 80, 81, 83, 87, + 90, 92, 98, 102, 78, 78, 57, 58, 79, 102, + 78, 88, 64, 86, 102, 78, 90, 95, 78, 102, + 102, 6, 8, 9, 100, 102, 104, 102, 63, 66, + 11, 90, 80, 98, 102, 68, 81, 97, 98, 98, + 90, 90, 66, 85, 95, 66, 66, 75, 104, 74, + 104, 66, 102, 63, 6, 7, 9, 54, 63, 66, + 9, 10, 55, 56, 87, 102, 63, 66, 57, 79, + 84, 85, 99, 102, 100, 100, 66, 83, 63, 82, + 95, 63, 66, 85, 63, 7, 102, 102, 66, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 67, + 89, 102, 39, 40, 41, 101, 102, 78, 63, 63, + 9, 63, 63, 63, 63, 67, 94, 94, 73, 62, + 73, 62, 104, 90, 95, 6, 7, 7, 7, 90, + 102, 55, 68, 83, 95, 100, 66, 42, 95, 65, + 9, 78, 64, 63, 85, 100, 101, 104, 104, 104, + 6, 7, 104, 104, 104, 104, 104, 63, 67, 63, + 98, 104, 47, 98, 98, 90, 90, 67, 67, 67, + 63, 93, 95, 102, 93, 93, 93, 67, 87, 67, + 67, 78, 63, 95, 82, 82, 90, 63, 78, 66, + 67, 104, 104, 89, 80, 63, 63, 78, 63, 63, + 90, 86, 65, 90, 40, 95, 98, 66, 91, 90, + 67, 63, 95, 63, 48, 49, 96, 63, 95, 66, + 63, 95, 104, 96, 67, 67, 63, 103, 104 }; #define yyerrok (yyerrstatus = 0) @@ -1974,7 +1972,7 @@ yyreduce: /* Line 1455 of yacc.c */ #line 219 "a.y" { - outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), NREG, &(yyvsp[(4) - (4)].gen)); + outcode((yyvsp[(1) - (4)].lval), Always, &(yyvsp[(2) - (4)].gen), 0, &(yyvsp[(4) - (4)].gen)); } break; @@ -2086,9 +2084,9 @@ yyreduce: case 42: /* Line 1455 of yacc.c */ -#line 299 "a.y" +#line 300 "a.y" { - (yyvsp[(7) - (9)].gen).type = D_REGREG; + (yyvsp[(7) - (9)].gen).type = D_REGREG2; (yyvsp[(7) - (9)].gen).offset = (yyvsp[(9) - (9)].lval); outcode((yyvsp[(1) - (9)].lval), (yyvsp[(2) - (9)].lval), &(yyvsp[(3) - (9)].gen), (yyvsp[(5) - (9)].gen).reg, &(yyvsp[(7) - (9)].gen)); } @@ -2097,43 +2095,52 @@ yyreduce: case 43: /* Line 1455 of yacc.c */ -#line 308 "a.y" +#line 309 "a.y" { - outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); + outcode((yyvsp[(1) - (2)].lval), Always, &(yyvsp[(2) - (2)].gen), NREG, &nullgen); } break; case 44: /* Line 1455 of yacc.c */ -#line 313 "a.y" +#line 316 "a.y" { - (yyval.lval) = Always; + outcode((yyvsp[(1) - (2)].lval), Always, &nullgen, NREG, &nullgen); } break; case 45: /* Line 1455 of yacc.c */ -#line 317 "a.y" +#line 321 "a.y" { - (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); + (yyval.lval) = Always; } break; case 46: /* Line 1455 of yacc.c */ -#line 321 "a.y" +#line 325 "a.y" + { + (yyval.lval) = ((yyvsp[(1) - (2)].lval) & ~C_SCOND) | (yyvsp[(2) - (2)].lval); + } + break; + + case 47: + +/* Line 1455 of yacc.c */ +#line 329 "a.y" { (yyval.lval) = (yyvsp[(1) - (2)].lval) | (yyvsp[(2) - (2)].lval); } break; - case 49: + case 50: /* Line 1455 of yacc.c */ -#line 330 "a.y" +#line 338 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2141,10 +2148,10 @@ yyreduce: } break; - case 50: + case 51: /* Line 1455 of yacc.c */ -#line 336 "a.y" +#line 344 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2155,10 +2162,10 @@ yyreduce: } break; - case 51: + case 52: /* Line 1455 of yacc.c */ -#line 345 "a.y" +#line 353 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2167,10 +2174,10 @@ yyreduce: } break; - case 52: + case 53: /* Line 1455 of yacc.c */ -#line 353 "a.y" +#line 361 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2178,30 +2185,30 @@ yyreduce: } break; - case 53: + case 54: /* Line 1455 of yacc.c */ -#line 359 "a.y" +#line 367 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).type = D_CONST; } break; - case 54: + case 55: /* Line 1455 of yacc.c */ -#line 364 "a.y" +#line 372 "a.y" { (yyval.gen) = (yyvsp[(4) - (4)].gen); (yyval.gen).type = D_OCONST; } break; - case 55: + case 56: /* Line 1455 of yacc.c */ -#line 369 "a.y" +#line 377 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2209,10 +2216,10 @@ yyreduce: } break; - case 57: + case 58: /* Line 1455 of yacc.c */ -#line 378 "a.y" +#line 386 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2220,10 +2227,10 @@ yyreduce: } break; - case 58: + case 59: /* Line 1455 of yacc.c */ -#line 384 "a.y" +#line 392 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2231,19 +2238,19 @@ yyreduce: } break; - case 59: + case 60: /* Line 1455 of yacc.c */ -#line 392 "a.y" +#line 400 "a.y" { (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval); } break; - case 60: + case 61: /* Line 1455 of yacc.c */ -#line 396 "a.y" +#line 404 "a.y" { int i; (yyval.lval)=0; @@ -2254,29 +2261,29 @@ yyreduce: } break; - case 61: + case 62: /* Line 1455 of yacc.c */ -#line 405 "a.y" +#line 413 "a.y" { (yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval); } break; - case 65: + case 66: /* Line 1455 of yacc.c */ -#line 414 "a.y" +#line 422 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).reg = (yyvsp[(3) - (4)].lval); } break; - case 66: + case 67: /* Line 1455 of yacc.c */ -#line 419 "a.y" +#line 427 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_PSR; @@ -2284,10 +2291,10 @@ yyreduce: } break; - case 67: + case 68: /* Line 1455 of yacc.c */ -#line 425 "a.y" +#line 433 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FPCR; @@ -2295,10 +2302,10 @@ yyreduce: } break; - case 68: + case 69: /* Line 1455 of yacc.c */ -#line 431 "a.y" +#line 439 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2306,10 +2313,10 @@ yyreduce: } break; - case 72: + case 73: /* Line 1455 of yacc.c */ -#line 442 "a.y" +#line 450 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); if((yyvsp[(1) - (1)].gen).name != D_EXTERN && (yyvsp[(1) - (1)].gen).name != D_STATIC) { @@ -2317,10 +2324,10 @@ yyreduce: } break; - case 73: + case 74: /* Line 1455 of yacc.c */ -#line 450 "a.y" +#line 458 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2329,10 +2336,10 @@ yyreduce: } break; - case 75: + case 76: /* Line 1455 of yacc.c */ -#line 460 "a.y" +#line 468 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2341,10 +2348,10 @@ yyreduce: } break; - case 77: + case 78: /* Line 1455 of yacc.c */ -#line 470 "a.y" +#line 478 "a.y" { (yyval.gen) = (yyvsp[(1) - (4)].gen); (yyval.gen).type = D_OREG; @@ -2352,10 +2359,10 @@ yyreduce: } break; - case 82: + case 83: /* Line 1455 of yacc.c */ -#line 483 "a.y" +#line 491 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2363,10 +2370,10 @@ yyreduce: } break; - case 83: + case 84: /* Line 1455 of yacc.c */ -#line 491 "a.y" +#line 499 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REG; @@ -2374,10 +2381,10 @@ yyreduce: } break; - case 84: + case 85: /* Line 1455 of yacc.c */ -#line 499 "a.y" +#line 507 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_REGREG; @@ -2386,10 +2393,10 @@ yyreduce: } break; - case 85: + case 86: /* Line 1455 of yacc.c */ -#line 508 "a.y" +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2397,10 +2404,10 @@ yyreduce: } break; - case 86: + case 87: /* Line 1455 of yacc.c */ -#line 514 "a.y" +#line 522 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2408,10 +2415,10 @@ yyreduce: } break; - case 87: + case 88: /* Line 1455 of yacc.c */ -#line 520 "a.y" +#line 528 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2419,10 +2426,10 @@ yyreduce: } break; - case 88: + case 89: /* Line 1455 of yacc.c */ -#line 526 "a.y" +#line 534 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SHIFT; @@ -2430,10 +2437,10 @@ yyreduce: } break; - case 89: + case 90: /* Line 1455 of yacc.c */ -#line 534 "a.y" +#line 542 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 16) print("register value out of range\n"); @@ -2441,10 +2448,10 @@ yyreduce: } break; - case 90: + case 91: /* Line 1455 of yacc.c */ -#line 540 "a.y" +#line 548 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 32) print("shift value out of range\n"); @@ -2452,19 +2459,19 @@ yyreduce: } break; - case 92: + case 93: /* Line 1455 of yacc.c */ -#line 549 "a.y" +#line 557 "a.y" { (yyval.lval) = REGPC; } break; - case 93: + case 94: /* Line 1455 of yacc.c */ -#line 553 "a.y" +#line 561 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2472,19 +2479,19 @@ yyreduce: } break; - case 95: + case 96: /* Line 1455 of yacc.c */ -#line 562 "a.y" +#line 570 "a.y" { (yyval.lval) = REGSP; } break; - case 97: + case 98: /* Line 1455 of yacc.c */ -#line 569 "a.y" +#line 577 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range\n"); @@ -2492,10 +2499,10 @@ yyreduce: } break; - case 100: + case 101: /* Line 1455 of yacc.c */ -#line 581 "a.y" +#line 589 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2503,10 +2510,10 @@ yyreduce: } break; - case 101: + case 102: /* Line 1455 of yacc.c */ -#line 587 "a.y" +#line 595 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FREG; @@ -2514,10 +2521,10 @@ yyreduce: } break; - case 102: + case 103: /* Line 1455 of yacc.c */ -#line 595 "a.y" +#line 603 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2527,10 +2534,10 @@ yyreduce: } break; - case 103: + case 104: /* Line 1455 of yacc.c */ -#line 603 "a.y" +#line 611 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2540,10 +2547,10 @@ yyreduce: } break; - case 104: + case 105: /* Line 1455 of yacc.c */ -#line 611 "a.y" +#line 619 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_OREG; @@ -2553,181 +2560,181 @@ yyreduce: } break; - case 105: + case 106: /* Line 1455 of yacc.c */ -#line 620 "a.y" +#line 628 "a.y" { (yyval.lval) = 0; } break; - case 106: + case 107: /* Line 1455 of yacc.c */ -#line 624 "a.y" +#line 632 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 107: + case 108: /* Line 1455 of yacc.c */ -#line 628 "a.y" +#line 636 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 112: + case 113: /* Line 1455 of yacc.c */ -#line 640 "a.y" +#line 648 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 113: + case 114: /* Line 1455 of yacc.c */ -#line 644 "a.y" +#line 652 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 114: + case 115: /* Line 1455 of yacc.c */ -#line 648 "a.y" +#line 656 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 115: + case 116: /* Line 1455 of yacc.c */ -#line 652 "a.y" +#line 660 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 116: + case 117: /* Line 1455 of yacc.c */ -#line 656 "a.y" +#line 664 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 117: + case 118: /* Line 1455 of yacc.c */ -#line 661 "a.y" +#line 669 "a.y" { (yyval.lval) = 0; } break; - case 118: + case 119: /* Line 1455 of yacc.c */ -#line 665 "a.y" +#line 673 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 120: + case 121: /* Line 1455 of yacc.c */ -#line 672 "a.y" +#line 680 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 121: + case 122: /* Line 1455 of yacc.c */ -#line 676 "a.y" +#line 684 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 122: + case 123: /* Line 1455 of yacc.c */ -#line 680 "a.y" +#line 688 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 123: + case 124: /* Line 1455 of yacc.c */ -#line 684 "a.y" +#line 692 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 124: + case 125: /* Line 1455 of yacc.c */ -#line 688 "a.y" +#line 696 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 125: + case 126: /* Line 1455 of yacc.c */ -#line 692 "a.y" +#line 700 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 126: + case 127: /* Line 1455 of yacc.c */ -#line 696 "a.y" +#line 704 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 127: + case 128: /* Line 1455 of yacc.c */ -#line 700 "a.y" +#line 708 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 128: + case 129: /* Line 1455 of yacc.c */ -#line 704 "a.y" +#line 712 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 129: + case 130: /* Line 1455 of yacc.c */ -#line 708 "a.y" +#line 716 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2736,7 +2743,7 @@ yyreduce: /* Line 1455 of yacc.c */ -#line 2740 "y.tab.c" +#line 2747 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/src/cmd/5a/y.tab.h b/src/cmd/5a/y.tab.h index 28039992c..1dd0cb08a 100644 --- a/src/cmd/5a/y.tab.h +++ b/src/cmd/5a/y.tab.h @@ -63,28 +63,29 @@ LTYPEM = 279, LTYPEN = 280, LTYPEBX = 281, - LCONST = 282, - LSP = 283, - LSB = 284, - LFP = 285, - LPC = 286, - LTYPEX = 287, - LR = 288, - LREG = 289, - LF = 290, - LFREG = 291, - LC = 292, - LCREG = 293, - LPSR = 294, - LFCR = 295, - LCOND = 296, - LS = 297, - LAT = 298, - LFCONST = 299, - LSCONST = 300, - LNAME = 301, - LLAB = 302, - LVAR = 303 + LTYPEPLD = 282, + LCONST = 283, + LSP = 284, + LSB = 285, + LFP = 286, + LPC = 287, + LTYPEX = 288, + LR = 289, + LREG = 290, + LF = 291, + LFREG = 292, + LC = 293, + LCREG = 294, + LPSR = 295, + LFCR = 296, + LCOND = 297, + LS = 298, + LAT = 299, + LFCONST = 300, + LSCONST = 301, + LNAME = 302, + LLAB = 303, + LVAR = 304 }; #endif /* Tokens. */ @@ -112,28 +113,29 @@ #define LTYPEM 279 #define LTYPEN 280 #define LTYPEBX 281 -#define LCONST 282 -#define LSP 283 -#define LSB 284 -#define LFP 285 -#define LPC 286 -#define LTYPEX 287 -#define LR 288 -#define LREG 289 -#define LF 290 -#define LFREG 291 -#define LC 292 -#define LCREG 293 -#define LPSR 294 -#define LFCR 295 -#define LCOND 296 -#define LS 297 -#define LAT 298 -#define LFCONST 299 -#define LSCONST 300 -#define LNAME 301 -#define LLAB 302 -#define LVAR 303 +#define LTYPEPLD 282 +#define LCONST 283 +#define LSP 284 +#define LSB 285 +#define LFP 286 +#define LPC 287 +#define LTYPEX 288 +#define LR 289 +#define LREG 290 +#define LF 291 +#define LFREG 292 +#define LC 293 +#define LCREG 294 +#define LPSR 295 +#define LFCR 296 +#define LCOND 297 +#define LS 298 +#define LAT 299 +#define LFCONST 300 +#define LSCONST 301 +#define LNAME 302 +#define LLAB 303 +#define LVAR 304 @@ -154,7 +156,7 @@ typedef union YYSTYPE /* Line 1676 of yacc.c */ -#line 158 "y.tab.h" +#line 160 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c index 9e74f515b..5ff4f633d 100644 --- a/src/cmd/5c/cgen.c +++ b/src/cmd/5c/cgen.c @@ -950,9 +950,9 @@ sugen(Node *n, Node *nn, int32 w) case OSTRUCT: /* - * rewrite so lhs has no fn call + * rewrite so lhs has no side effect. */ - if(nn != Z && nn->complex >= FNX) { + if(nn != Z && side(nn)) { nod1 = *n; nod1.type = typ(TIND, n->type); regret(&nod2, &nod1); diff --git a/src/cmd/5c/doc.go b/src/cmd/5c/doc.go index 0874293bf..7291d45f4 100644 --- a/src/cmd/5c/doc.go +++ b/src/cmd/5c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 5c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the ARM, referred to by these tools as arm. */ -package documentation +package main diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index 20652682b..a0fc63c60 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -297,6 +297,7 @@ void patch(Prog*, int32); int sconst(Node*); int sval(int32); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c diff --git a/src/cmd/5c/peep.c b/src/cmd/5c/peep.c index 3a905f099..2f902e02a 100644 --- a/src/cmd/5c/peep.c +++ b/src/cmd/5c/peep.c @@ -824,7 +824,7 @@ xtramodes(Reg *r, Adr *a) Adr v; p = r->prog; - if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + if(p->as == AMOVB && p->from.type == D_OREG) /* byte load */ return 0; v = *a; v.type = D_REG; @@ -1067,6 +1067,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ABLT: case ABGT: case ABLE: + case APLD: if(s != A) { if(copysub(&p->from, v, s, 1)) return 1; diff --git a/src/cmd/5c/reg.c b/src/cmd/5c/reg.c index 1ccf74a35..42c5193de 100644 --- a/src/cmd/5c/reg.c +++ b/src/cmd/5c/reg.c @@ -66,12 +66,6 @@ rcmp(const void *a1, const void *a2) void regopt(Prog *p) { - USED(p); - // TODO(kaib): optimizer disabled because it smashes R8 when running out of registers - // the disable is unconventionally here because the call is in common code shared by 5c/6c/8c - return; - -#ifdef NOTDEF Reg *r, *r1, *r2; Prog *p1; int i, z; @@ -191,6 +185,14 @@ regopt(Prog *p) break; /* + * right side read + */ + case APLD: + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + break; + + /* * funny */ case ABL: @@ -492,7 +494,6 @@ brk: r1->link = freer; freer = firstr; } -#endif } void @@ -1149,12 +1150,13 @@ addreg(Adr *a, int rn) * 1 R1 * ... ... * 10 R10 + * 12 R12 */ int32 RtoB(int r) { - if(r < 2 || r >= REGTMP-2) // excluded R9 and R10 for m and g + if(r < 2 || (r >= REGTMP-2 && r != 12)) // excluded R9 and R10 for m and g, but not R12 return 0; return 1L << r; } @@ -1162,7 +1164,7 @@ RtoB(int r) int BtoR(int32 b) { - b &= 0x01fcL; // excluded R9 and R10 for m and g + b &= 0x11fcL; // excluded R9 and R10 for m and g, but not R12 if(b == 0) return 0; return bitno(b); @@ -1173,7 +1175,7 @@ BtoR(int32 b) * 18 F2 * 19 F3 * ... ... - * 23 F7 + * 31 F15 */ int32 FtoB(int f) @@ -1188,7 +1190,7 @@ int BtoF(int32 b) { - b &= 0xfc0000L; + b &= 0xfffc0000L; if(b == 0) return 0; return bitno(b) - 16; diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index 7268f9af2..87b77518b 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -393,18 +393,12 @@ outcode(void) } Bprint(&outbuf, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&outbuf, "\n"); Bprint(&outbuf, "$$ // exports\n\n"); Bprint(&outbuf, "$$ // local types\n\n"); - Bprint(&outbuf, "$$ // dynimport\n"); - for(i=0; i<ndynimp; i++) - Bprint(&outbuf, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path); - Bprint(&outbuf, "\n$$ // dynexport\n"); - for(i=0; i<ndynexp; i++) - Bprint(&outbuf, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote); + Bprint(&outbuf, "$$ // cgo\n"); + Bprint(&outbuf, "%s", fmtstrflush(&pragcgobuf)); Bprint(&outbuf, "\n$$\n\n"); } Bprint(&outbuf, "!\n"); @@ -472,12 +466,38 @@ outhist(Biobuf *b) char *p, *q, *op, c; Prog pg; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + tofree = nil; pg = zprog; pg.as = AHISTORY; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -525,6 +545,11 @@ outhist(Biobuf *b) pg.to.type = D_CONST; zwrite(b, &pg, 0, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -566,7 +591,8 @@ zaddr(char *bp, Adr *a, int s) bp[1] = a->reg; bp[2] = s; bp[3] = a->name; - bp += 4; + bp[4] = 0; + bp += 5; switch(a->type) { default: diag(Z, "unknown type %d in zaddr", a->type); diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index 1a189e3af..b8675fe60 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -1176,17 +1176,15 @@ patch(Prog *op, int32 pc) void gpseudo(int a, Sym *s, Node *n) { - nextpc(); p->as = a; p->from.type = D_OREG; p->from.sym = s; p->from.name = D_EXTERN; - if(a == ATEXT) { + if(a == ATEXT || a == AGLOBL) { p->reg = textflag; textflag = 0; - } else if(a == AGLOBL) - p->reg = 0; + } if(s->class == CSTATIC) p->from.name = D_STATIC; naddr(n, &p->to); @@ -1194,6 +1192,18 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APLD, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index cccef94c9..9e35f9566 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -15,8 +15,8 @@ void cgen(Node *n, Node *res) { Node *nl, *nr, *r; - Node n1, n2, n3, f0, f1; - int a, w; + Node n1, n2, f0, f1; + int a, w, rg; Prog *p1, *p2, *p3; Addr addr; @@ -30,6 +30,27 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); + return; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); + return; + } + while(n->op == OCONVNOP) n = n->left; @@ -156,6 +177,7 @@ cgen(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -171,7 +193,7 @@ cgen(Node *n, Node *res) switch(n->op) { default: dump("cgen", n); - fatal("cgen: unknown op %N", n); + fatal("cgen: unknown op %+hN", n); break; case OREAL: @@ -190,12 +212,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AB, T); + p3 = gbranch(AB, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); goto ret; @@ -218,13 +240,10 @@ cgen(Node *n, Node *res) case OMINUS: regalloc(&n1, nl->type, N); cgen(nl, &n1); - nodconst(&n3, nl->type, 0); - regalloc(&n2, nl->type, res); - gmove(&n3, &n2); - gins(optoas(OSUB, nl->type), &n1, &n2); - gmove(&n2, res); + nodconst(&n2, nl->type, 0); + gins(optoas(OMINUS, nl->type), &n2, &n1); + gmove(&n1, res); regfree(&n1); - regfree(&n2); goto ret; // symmetric binary @@ -241,9 +260,14 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + + case OLROT: case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + cgen_shift(n->op, n->bounded, nl, nr, res); break; case OCONV: @@ -284,17 +308,11 @@ cgen(Node *n, Node *res) break; case OITAB: - // itable of interface value + // interface table is first word of interface value igen(nl, &n1, res); - n1.op = OREGISTER; // was OINDREG - regalloc(&n2, n->type, &n1); - n1.op = OINDREG; n1.type = n->type; - n1.xoffset = 0; - gmove(&n1, &n2); - gmove(&n2, res); + gmove(&n1, res); regfree(&n1); - regfree(&n2); break; case OLEN: @@ -305,11 +323,8 @@ cgen(Node *n, Node *res) cgen(nl, &n1); nodconst(&n2, types[tptr], 0); - regalloc(&n3, n2.type, N); - gmove(&n2, &n3); - gcmp(optoas(OCMP, types[tptr]), &n1, &n3); - regfree(&n3); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + gcmp(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -325,15 +340,10 @@ cgen(Node *n, Node *res) if(istype(nl->type, TSTRING) || isslice(nl->type)) { // both slice and string have len one pointer into the struct. igen(nl, &n1, res); - n1.op = OREGISTER; // was OINDREG - regalloc(&n2, types[TUINT32], &n1); - n1.op = OINDREG; n1.type = types[TUINT32]; - n1.xoffset = Array_nel; - gmove(&n1, &n2); - gmove(&n2, res); + n1.xoffset += Array_nel; + gmove(&n1, res); regfree(&n1); - regfree(&n2); break; } fatal("cgen: OLEN: unknown type %lT", nl->type); @@ -347,11 +357,8 @@ cgen(Node *n, Node *res) cgen(nl, &n1); nodconst(&n2, types[tptr], 0); - regalloc(&n3, n2.type, N); - gmove(&n2, &n3); - gcmp(optoas(OCMP, types[tptr]), &n1, &n3); - regfree(&n3); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + gcmp(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -366,11 +373,9 @@ cgen(Node *n, Node *res) break; } if(isslice(nl->type)) { - regalloc(&n1, types[tptr], res); - agen(nl, &n1); - n1.op = OINDREG; + igen(nl, &n1, res); n1.type = types[TUINT32]; - n1.xoffset = Array_cap; + n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); break; @@ -383,7 +388,22 @@ cgen(Node *n, Node *res) break; case OCALLMETH: - cgen_callmeth(n, 0); + case OCALLFUNC: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + rg = -1; + if(n->ullman >= UINF) { + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + rg = res->val.u.reg; + reg[rg]--; + } + } + if(n->op == OCALLMETH) + cgen_callmeth(n, 0); + else + cgen_call(n, 0); + if(rg >= 0) + reg[rg]++; cgen_callret(n, res); break; @@ -392,11 +412,6 @@ cgen(Node *n, Node *res) cgen_callret(n, res); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_callret(n, res); - break; - case OMOD: case ODIV: a = optoas(n->op, nl->type); @@ -416,18 +431,43 @@ abop: // asymmetric binary if(nl->ullman >= nr->ullman) { regalloc(&n1, nl->type, res); cgen(nl, &n1); - regalloc(&n2, nr->type, N); - cgen(nr, &n2); + switch(n->op) { + case OADD: + case OSUB: + case OAND: + case OOR: + case OXOR: + if(smallintconst(nr)) { + n2 = *nr; + break; + } + default: + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + } } else { - regalloc(&n2, nr->type, res); - cgen(nr, &n2); + switch(n->op) { + case OADD: + case OSUB: + case OAND: + case OOR: + case OXOR: + if(smallintconst(nr)) { + n2 = *nr; + break; + } + default: + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); gmove(&n1, res); regfree(&n1); - regfree(&n2); + if(n2.op != OLITERAL) + regfree(&n2); goto ret; flt: // floating-point. @@ -478,7 +518,7 @@ ret: * returns Prog* to patch to panic call. */ Prog* -cgenindex(Node *n, Node *res) +cgenindex(Node *n, Node *res, int bounded) { Node tmp, lo, hi, zero, n1, n2; @@ -491,7 +531,7 @@ cgenindex(Node *n, Node *res) cgen(n, &tmp); split64(&tmp, &lo, &hi); gmove(&lo, res); - if(debug['B']) { + if(bounded) { splitclean(); return nil; } @@ -504,7 +544,7 @@ cgenindex(Node *n, Node *res) regfree(&n2); regfree(&n1); splitclean(); - return gbranch(ABNE, T); + return gbranch(ABNE, T, -1); } /* @@ -514,11 +554,8 @@ cgenindex(Node *n, Node *res) void agen(Node *n, Node *res) { - Node *nl, *nr; - Node n1, n2, n3, n4, n5, tmp; - Prog *p1, *p2; - uint32 w; - uint64 v; + Node *nl; + Node n1, n2, n3; int r; if(debug['g']) { @@ -531,6 +568,21 @@ agen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(AMOVW, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + if(n->addable) { memset(&n1, 0, sizeof n1); n1.op = OADDR; @@ -543,11 +595,10 @@ agen(Node *n, Node *res) } nl = n->left; - nr = n->right; switch(n->op) { default: - fatal("agen: unknown op %N", n); + fatal("agen: unknown op %+hN", n); break; case OCALLMETH: @@ -575,16 +626,305 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + + case OINDEX: + agenr(n, &n1, res); + gmove(&n1, res); + regfree(&n1); + break; + + case ONAME: + // should only get here with names in this func. + if(n->funcdepth > 0 && n->funcdepth != funcdepth) { + dump("bad agen", n); + fatal("agen: bad ONAME funcdepth %d != %d", + n->funcdepth, funcdepth); + } + + // should only get here for heap vars or paramref + if(!(n->class & PHEAP) && n->class != PPARAMREF) { + dump("bad agen", n); + fatal("agen: bad ONAME class %#x", n->class); + } + cgen(n->heapaddr, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case OIND: + cgen(nl, res); + break; + + case ODOT: + agen(nl, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[TINT32], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + + case ODOTPTR: + cgen(nl, res); + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(nl->type->type->width >= unmappedzero) { + regalloc(&n1, types[tptr], N); + gmove(res, &n1); + regalloc(&n2, types[TUINT8], &n1); + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gmove(&n1, &n2); + regfree(&n1); + regfree(&n2); + } + nodconst(&n1, types[TINT32], n->xoffset); + regalloc(&n2, n1.type, N); + regalloc(&n3, types[tptr], N); + gmove(&n1, &n2); + gmove(res, &n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + gmove(&n3, res); + regfree(&n2); + regfree(&n3); + } + break; + } + +ret: + ; +} + +/* + * generate: + * newreg = &n; + * res = newreg + * + * on exit, a has been changed to be *newreg. + * caller must regfree(a). + */ +void +igen(Node *n, Node *a, Node *res) +{ + Node n1, n2; + int r; + + if(debug['g']) { + dump("\nigen-n", n); + } + switch(n->op) { + case ONAME: + if((n->class&PHEAP) || n->class == PPARAMREF) + break; + *a = *n; + return; + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != REGSP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + if(n->left->addable + || n->left->op == OCALLFUNC + || n->left->op == OCALLMETH + || n->left->op == OCALLINTER) { + // igen-able nodes. + igen(n->left, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + } else { + regalloc(a, types[tptr], res); + cgen(n->left, a); + } + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + regalloc(&n1, types[tptr], N); + gmove(a, &n1); + regalloc(&n2, types[TUINT8], &n1); + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gmove(&n1, &n2); + regfree(&n1); + regfree(&n2); + } + } + a->op = OINDREG; + a->xoffset = n->xoffset; + a->type = n->type; + return; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + // Release res so that it is available for cgen_call. + // Pick it up again after the call. + r = -1; + if(n->ullman >= UINF) { + if(res != N && (res->op == OREGISTER || res->op == OINDREG)) { + r = res->val.u.reg; + reg[r]--; + } + } + switch(n->op) { + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } + if(r >= 0) + reg[r]++; + regalloc(a, types[tptr], res); + cgen_aret(n, a); + a->op = OINDREG; + a->type = n->type; + return; + } + + agenr(n, a, res); + a->op = OINDREG; + a->type = n->type; +} + +/* + * allocate a register in res and generate + * newreg = &n + * The caller must call regfree(a). + */ +void +cgenr(Node *n, Node *a, Node *res) +{ + Node n1; + + if(debug['g']) + dump("cgenr-n", n); + + if(isfat(n->type)) + fatal("cgenr on fat node"); + + if(n->addable) { + regalloc(a, types[tptr], res); + gmove(n, a); + return; + } + + switch(n->op) { + case ONAME: + case ODOT: + case ODOTPTR: + case OINDEX: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, n->type, res); + cgen(n, a); + break; + } +} + +/* + * generate: + * newreg = &n; + * + * caller must regfree(a). + */ +void +agenr(Node *n, Node *a, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3, n4, tmp; + Prog *p1, *p2; + uint32 w; + uint64 v; + int bounded; + + if(debug['g']) + dump("agenr-n", n); + + nl = n->left; + nr = n->right; + + switch(n->op) { + case ODOT: + case ODOTPTR: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + agen(&n1, a); + regfree(&n1); + break; + + case OIND: + cgenr(n->left, a, res); + break; + case OINDEX: p2 = nil; // to be patched to panicindex. w = n->type->width; + bounded = debug['B'] || n->bounded; if(nr->addable) { if(!isconst(nr, CTINT)) tempname(&tmp, types[TINT32]); if(!isconst(nl, CTSTR)) agenr(nl, &n3, res); if(!isconst(nr, CTINT)) { - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } @@ -592,17 +932,16 @@ agen(Node *n, Node *res) if(nl->addable) { if(!isconst(nr, CTINT)) { tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + agenr(nl, &n3, res); } } else { tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = cgenindex(nr, &tmp, bounded); nr = &tmp; if(!isconst(nl, CTSTR)) agenr(nl, &n3, res); @@ -614,26 +953,37 @@ agen(Node *n, Node *res) // i is in &n1 (if not constant) // w is width + // explicit check for nil if array is large enough + // that we might derive too big a pointer. + if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { + regalloc(&n4, types[tptr], N); + gmove(&n3, &n4); + regalloc(&tmp, types[TUINT8], &n4); + n4.op = OINDREG; + n4.type = types[TUINT8]; + n4.xoffset = 0; + gmove(&n4, &tmp); + regfree(&n4); + regfree(&tmp); + } + // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) fatal("constant string constant index"); v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; regalloc(&n4, n1.type, N); - cgen(&n1, &n4); + gmove(&n1, &n4); nodconst(&n2, types[TUINT32], v); - regalloc(&n5, n2.type, N); - gmove(&n2, &n5); - gcmp(optoas(OCMP, types[TUINT32]), &n4, &n5); + gcmp(optoas(OCMP, types[TUINT32]), &n4, &n2); regfree(&n4); - regfree(&n5); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); ginscall(panicindex, 0); patch(p1, pc); } @@ -646,13 +996,8 @@ agen(Node *n, Node *res) } nodconst(&n2, types[tptr], v*w); - regalloc(&n4, n2.type, N); - gmove(&n2, &n4); - gins(optoas(OADD, types[tptr]), &n4, &n3); - regfree(&n4); - - gmove(&n3, res); - regfree(&n3); + gins(optoas(OADD, types[tptr]), &n2, &n3); + *a = n3; break; } @@ -660,25 +1005,24 @@ agen(Node *n, Node *res) gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - regalloc(&n4, types[TUINT32], N); if(isconst(nl, CTSTR)) { - nodconst(&n1, types[TUINT32], nl->val.u.sval->len); - gmove(&n1, &n4); + nodconst(&n4, types[TUINT32], nl->val.u.sval->len); } else if(isslice(nl->type) || nl->type->etype == TSTRING) { n1 = n3; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; - cgen(&n1, &n4); - } else { - nodconst(&n1, types[TUINT32], nl->type->bound); + regalloc(&n4, types[TUINT32], N); gmove(&n1, &n4); + } else { + nodconst(&n4, types[TUINT32], nl->type->bound); } gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4); - regfree(&n4); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + if(n4.op == OREGISTER) + regfree(&n4); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p2) patch(p2, pc); ginscall(panicindex, 0); @@ -713,7 +1057,7 @@ agen(Node *n, Node *res) else if(w == 4) gshift(AADD, &n2, SHIFT_LL, 2, &n3); else if(w == 8) - gshift(AADD, &n2, SHIFT_LL, 3, &n3); + gshift(AADD, &n2, SHIFT_LL, 3, &n3); } else { regalloc(&n4, types[TUINT32], N); nodconst(&n1, types[TUINT32], w); @@ -721,122 +1065,21 @@ agen(Node *n, Node *res) gins(optoas(OMUL, types[TUINT32]), &n4, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); regfree(&n4); - gmove(&n3, res); } - gmove(&n3, res); + *a = n3; regfree(&n2); - regfree(&n3); break; - case ONAME: - // should only get here with names in this func. - if(n->funcdepth > 0 && n->funcdepth != funcdepth) { - dump("bad agen", n); - fatal("agen: bad ONAME funcdepth %d != %d", - n->funcdepth, funcdepth); - } - - // should only get here for heap vars or paramref - if(!(n->class & PHEAP) && n->class != PPARAMREF) { - dump("bad agen", n); - fatal("agen: bad ONAME class %#x", n->class); - } - cgen(n->heapaddr, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[TINT32], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } - break; - - case OIND: - cgen(nl, res); - break; - - case ODOT: - agen(nl, res); - if(n->xoffset != 0) { - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[TINT32], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } - break; - - case ODOTPTR: - cgen(nl, res); - if(n->xoffset != 0) { - // explicit check for nil if struct is large enough - // that we might derive too big a pointer. - if(nl->type->type->width >= unmappedzero) { - regalloc(&n1, types[tptr], N); - gmove(res, &n1); - p1 = gins(AMOVW, &n1, &n1); - p1->from.type = D_OREG; - p1->from.offset = 0; - regfree(&n1); - } - nodconst(&n1, types[TINT32], n->xoffset); - regalloc(&n2, n1.type, N); - regalloc(&n3, types[tptr], N); - gmove(&n1, &n2); - gmove(res, &n3); - gins(optoas(OADD, types[tptr]), &n2, &n3); - gmove(&n3, res); - regfree(&n2); - regfree(&n3); - } + default: + regalloc(a, types[tptr], res); + agen(n, a); break; } - -ret: - ; } -/* - * generate: - * newreg = &n; - * res = newreg - * - * on exit, a has been changed to be *newreg. - * caller must regfree(a). - */ void -igen(Node *n, Node *a, Node *res) -{ - regalloc(a, types[tptr], res); - agen(n, a); - a->op = OINDREG; - a->type = n->type; -} - -/* - * generate: - * newreg = &n; - * - * caller must regfree(a). - */ -void -agenr(Node *n, Node *a, Node *res) -{ - regalloc(a, types[tptr], res); - agen(n, a); -} - -void -gencmp0(Node *n, Type *t, int o, Prog *to) +gencmp0(Node *n, Type *t, int o, int likely, Prog *to) { Node n1, n2, n3; int a; @@ -853,7 +1096,7 @@ gencmp0(Node *n, Type *t, int o, Prog *to) } else gins(ATST, &n1, N); a = optoas(o, t); - patch(gbranch(a, t), to); + patch(gbranch(a, t, likely), to); regfree(&n1); } @@ -862,7 +1105,7 @@ gencmp0(Node *n, Type *t, int o, Prog *to) * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *r; @@ -900,13 +1143,13 @@ bgen(Node *n, int true, Prog *to) a = ONE; if(!true) a = OEQ; - gencmp0(n, n->type, a, to); + gencmp0(n, n->type, a, likely, to); goto ret; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AB, T), to); + patch(gbranch(AB, T, 0), to); goto ret; case OANDAND: @@ -914,12 +1157,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AB, T); - p2 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); + p2 = gbranch(AB, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AB, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AB, T, 0); patch(p1, to); patch(p2, pc); goto ret; @@ -929,8 +1172,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); goto ret; case OEQ: @@ -952,7 +1195,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); goto ret; case OEQ: @@ -965,14 +1208,14 @@ bgen(Node *n, int true, Prog *to) if(!true) { if(isfloat[nl->type->etype]) { // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AB, T); - p2 = gbranch(AB, T); + p1 = gbranch(AB, T, 0); + p2 = gbranch(AB, T, 0); patch(p1, pc); ll = n->ninit; n->ninit = nil; - bgen(n, 1, p2); + bgen(n, 1, -likely, p2); n->ninit = ll; - patch(gbranch(AB, T), to); + patch(gbranch(AB, T, 0), to); patch(p2, pc); goto ret; } @@ -995,34 +1238,12 @@ bgen(Node *n, int true, Prog *to) break; } - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - gencmp0(&n2, types[tptr], a, to); - regfree(&n1); - break; - -#ifdef NOTDEF - a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - regalloc(&n3, types[tptr], N); - regalloc(&n4, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - gmove(&n2, &n4); - nodconst(&tmp, types[tptr], 0); - gmove(&tmp, &n3); - gcmp(optoas(OCMP, types[tptr]), &n4, &n3); - patch(gbranch(a, types[tptr]), to); - regfree(&n4); - regfree(&n3); + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; + gencmp0(&n1, types[tptr], a, likely, to); regfree(&n1); break; -#endif } if(isinter(nl->type)) { @@ -1032,38 +1253,16 @@ bgen(Node *n, int true, Prog *to) break; } - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; - gencmp0(&n2, types[tptr], a, to); - regfree(&n1); - break; - -#ifdef NOTDEF - a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - regalloc(&n3, types[tptr], N); - regalloc(&n4, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; - gmove(&n2, &n4); - nodconst(&tmp, types[tptr], 0); - gmove(&tmp, &n3); - gcmp(optoas(OCMP, types[tptr]), &n4, &n3); - patch(gbranch(a, types[tptr]), to); + igen(nl, &n1, N); + n1.type = types[tptr]; + n1.xoffset += 0; + gencmp0(&n1, types[tptr], a, likely, to); regfree(&n1); - regfree(&n3); - regfree(&n4); break; -#endif } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -1078,17 +1277,17 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); nr = &n2; } - cmp64(nl, nr, a, to); + cmp64(nl, nr, a, likely, to); break; } if(nr->op == OLITERAL) { if(isconst(nr, CTINT) && mpgetfix(nr->val.u.xval) == 0) { - gencmp0(nl, nl->type, a, to); + gencmp0(nl, nl->type, a, likely, to); break; } if(nr->val.ctype == CTNIL) { - gencmp0(nl, nl->type, a, to); + gencmp0(nl, nl->type, a, likely, to); break; } } @@ -1110,7 +1309,7 @@ bgen(Node *n, int true, Prog *to) cgen(&tmp, &n1); gcmp(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); + patch(gbranch(a, nr->type, likely), to); regfree(&n1); regfree(&n2); @@ -1131,14 +1330,17 @@ bgen(Node *n, int true, Prog *to) gcmp(optoas(OCMP, nr->type), &n1, &n2); if(isfloat[nl->type->etype]) { - p1 = gbranch(ABVS, nr->type); - patch(gbranch(a, nr->type), to); - if(n->op == ONE) + if(n->op == ONE) { + p1 = gbranch(ABVS, nr->type, likely); + patch(gbranch(a, nr->type, likely), to); patch(p1, to); - else + } else { + p1 = gbranch(ABVS, nr->type, -likely); + patch(gbranch(a, nr->type, likely), to); patch(p1, pc); + } } else { - patch(gbranch(a, nr->type), to); + patch(gbranch(a, nr->type, likely), to); } regfree(&n1); regfree(&n2); @@ -1241,6 +1443,10 @@ sgen(Node *n, Node *res, int64 w) return; } + // Avoid taking the address for simple enough types. + if(componentgen(n, res)) + return; + // determine alignment. // want to avoid unaligned access, so have to use // smaller operations for less aligned types. @@ -1284,16 +1490,14 @@ sgen(Node *n, Node *res, int64 w) if(osrc < odst && odst < osrc+w) dir = -dir; - regalloc(&dst, types[tptr], res); if(n->ullman >= res->ullman) { - agen(n, &dst); // temporarily use dst + agenr(n, &dst, res); // temporarily use dst regalloc(&src, types[tptr], N); gins(AMOVW, &dst, &src); agen(res, &dst); } else { - agen(res, &dst); - regalloc(&src, types[tptr], N); - agen(n, &src); + agenr(res, &dst, res); + agenr(n, &src, N); } regalloc(&tmp, types[TUINT32], N); @@ -1338,7 +1542,7 @@ sgen(Node *n, Node *res, int64 w) p = gins(ACMP, &src, N); raddr(&nend, p); - patch(gbranch(ABNE, T), ploop); + patch(gbranch(ABNE, T, 0), ploop); regfree(&nend); } else { while(c-- > 0) { @@ -1358,3 +1562,157 @@ sgen(Node *n, Node *res, int64 w) regfree(&src); regfree(&tmp); } + +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * nr is N when assigning a zero value. + * return 1 if can do, 0 if cant. + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr, tmp; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } else { + // When zeroing, prepare a register containing zero. + nodconst(&tmp, nl->type, 0); + regalloc(&nodr, types[TUINT], N); + gmove(&tmp, &nodr); + freer = 1; + } + + switch(nl->type->etype) { + case TARRAY: + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } + gmove(&nodr, &nodl); + + goto yes; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/5g/cgen64.c b/src/cmd/5g/cgen64.c index 1235d1ace..ef11e2adb 100644 --- a/src/cmd/5g/cgen64.c +++ b/src/cmd/5g/cgen64.c @@ -94,6 +94,7 @@ cgen64(Node *n, Node *res) case OAND: case OOR: case OXOR: + case OLROT: // binary operators. // common setup below. break; @@ -175,7 +176,7 @@ cgen64(Node *n, Node *res) p1->from.type = D_REG; p1->from.reg = bl.val.u.reg; p1->reg = ch.val.u.reg; - p1->to.type = D_REGREG; + p1->to.type = D_REGREG2; p1->to.reg = ah.val.u.reg; p1->to.offset = ah.val.u.reg; //print("%P\n", p1); @@ -185,7 +186,7 @@ cgen64(Node *n, Node *res) p1->from.type = D_REG; p1->from.reg = bh.val.u.reg; p1->reg = cl.val.u.reg; - p1->to.type = D_REGREG; + p1->to.type = D_REGREG2; p1->to.reg = ah.val.u.reg; p1->to.offset = ah.val.u.reg; //print("%P\n", p1); @@ -197,6 +198,47 @@ cgen64(Node *n, Node *res) break; + case OLROT: + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + v = mpgetfix(r->val.u.xval); + regalloc(&bl, lo1.type, N); + regalloc(&bh, hi1.type, N); + if(v >= 32) { + // reverse during load to do the first 32 bits of rotate + v -= 32; + gins(AMOVW, &hi1, &bl); + gins(AMOVW, &lo1, &bh); + } else { + gins(AMOVW, &hi1, &bh); + gins(AMOVW, &lo1, &bl); + } + if(v == 0) { + gins(AMOVW, &bh, &ah); + gins(AMOVW, &bl, &al); + } else { + // rotate by 1 <= v <= 31 + // MOVW bl<<v, al + // MOVW bh<<v, ah + // OR bl>>(32-v), ah + // OR bh>>(32-v), al + gshift(AMOVW, &bl, SHIFT_LL, v, &al); + gshift(AMOVW, &bh, SHIFT_LL, v, &ah); + gshift(AORR, &bl, SHIFT_LR, 32-v, &ah); + gshift(AORR, &bh, SHIFT_LR, 32-v, &al); + } + regfree(&bl); + regfree(&bh); + break; + case OLSH: regalloc(&bl, lo1.type, N); regalloc(&bh, hi1.type, N); @@ -243,7 +285,7 @@ cgen64(Node *n, Node *res) split64(r, &cl, &ch); gmove(&ch, &s); gins(ATST, &s, N); - p6 = gbranch(ABNE, T); + p6 = gbranch(ABNE, T, 0); gmove(&cl, &s); splitclean(); } else { @@ -257,7 +299,7 @@ cgen64(Node *n, Node *res) p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bh, &ah); p1->scond = C_SCOND_EQ; - p2 = gbranch(ABEQ, T); + p2 = gbranch(ABEQ, T, 0); // shift is < 32 nodconst(&n1, types[TUINT32], 32); @@ -281,14 +323,14 @@ cgen64(Node *n, Node *res) p1->scond = C_SCOND_LO; // BLO end - p3 = gbranch(ABLO, T); + p3 = gbranch(ABLO, T, 0); // shift == 32 p1 = gins(AEOR, &al, &al); p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bl, &ah); p1->scond = C_SCOND_EQ; - p4 = gbranch(ABEQ, T); + p4 = gbranch(ABEQ, T, 0); // shift is < 64 nodconst(&n1, types[TUINT32], 64); @@ -311,7 +353,7 @@ cgen64(Node *n, Node *res) p1 = gregshift(AMOVW, &bl, SHIFT_LL, &s, &ah); p1->scond = C_SCOND_LO; - p5 = gbranch(ABLO, T); + p5 = gbranch(ABLO, T, 0); // shift >= 64 if (p6 != P) patch(p6, pc); @@ -406,7 +448,7 @@ olsh_break: else p1 = gins(AEOR, &ah, &ah); p1->scond = C_SCOND_NE; - p6 = gbranch(ABNE, T); + p6 = gbranch(ABNE, T, 0); gmove(&cl, &s); splitclean(); } else { @@ -420,7 +462,7 @@ olsh_break: p1->scond = C_SCOND_EQ; p1 = gins(AMOVW, &bh, &ah); p1->scond = C_SCOND_EQ; - p2 = gbranch(ABEQ, T); + p2 = gbranch(ABEQ, T, 0); // check if shift is < 32 nodconst(&n1, types[TUINT32], 32); @@ -449,7 +491,7 @@ olsh_break: p1->scond = C_SCOND_LO; // BLO end - p3 = gbranch(ABLO, T); + p3 = gbranch(ABLO, T, 0); // shift == 32 p1 = gins(AMOVW, &bh, &al); @@ -458,7 +500,7 @@ olsh_break: gshift(AMOVW, &bh, SHIFT_AR, 31, &ah); else gins(AEOR, &ah, &ah); - p4 = gbranch(ABEQ, T); + p4 = gbranch(ABEQ, T, 0); // check if shift is < 64 nodconst(&n1, types[TUINT32], 64); @@ -484,7 +526,7 @@ olsh_break: } // BLO end - p5 = gbranch(ABLO, T); + p5 = gbranch(ABLO, T, 0); // s >= 64 if(p6 != P) @@ -633,7 +675,7 @@ orsh_break: * nl is memory; nr is constant or memory. */ void -cmp64(Node *nl, Node *nr, int op, Prog *to) +cmp64(Node *nl, Node *nr, int op, int likely, Prog *to) { Node lo1, hi1, lo2, hi2, r1, r2; Prog *br; @@ -663,14 +705,14 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // beq to // L: - br = gbranch(ABNE, T); + br = gbranch(ABNE, T, -likely); break; case ONE: // cmp hi // bne to // cmp lo // bne to - patch(gbranch(ABNE, T), to); + patch(gbranch(ABNE, T, likely), to); break; case OGE: case OGT: @@ -680,8 +722,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // bge to (or bgt to) // L: - patch(gbranch(optoas(OGT, t), T), to); - br = gbranch(optoas(OLT, t), T); + patch(gbranch(optoas(OGT, t), T, likely), to); + br = gbranch(optoas(OLT, t), T, -likely); break; case OLE: case OLT: @@ -691,8 +733,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // ble to (or jlt to) // L: - patch(gbranch(optoas(OLT, t), T), to); - br = gbranch(optoas(OGT, t), T); + patch(gbranch(optoas(OLT, t), T, likely), to); + br = gbranch(optoas(OGT, t), T, -likely); break; } @@ -707,7 +749,7 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) regfree(&r2); // jump again - patch(gbranch(optoas(op, t), T), to); + patch(gbranch(optoas(op, t), T, likely), to); // point first branch down here if appropriate if(br != P) diff --git a/src/cmd/5g/doc.go b/src/cmd/5g/doc.go index 5a4a772fb..aebdcab71 100644 --- a/src/cmd/5g/doc.go +++ b/src/cmd/5g/doc.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. +// +build ignore + /* 5g is the version of the gc compiler for the ARM. @@ -10,4 +12,4 @@ The $GOARCH for these tools is arm. It reads .go files and outputs .5 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 070804217..1fbf633f9 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 4; + widthint = 4; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index 7dbf3beec..45a9a887e 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -15,32 +15,36 @@ struct Addr { int32 offset; int32 offset2; - double dval; - Prog* branch; - char sval[NSNAME]; + + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* sym; + Sym* gotype; Node* node; int width; uchar type; char name; uchar reg; - char pun; uchar etype; }; #define A ((Addr*)0) struct Prog { - short as; // opcode uint32 loc; // pc offset in this func uint32 lineno; // source line that generated this - Addr from; // src address - Addr to; // dst address Prog* link; // next instruction in this func void* regp; // points to enclosing Reg struct + short as; // opcode uchar reg; // doubles as width in DATA op uchar scond; + Addr from; // src address + Addr to; // dst address }; #define TEXTFLAG reg @@ -78,22 +82,20 @@ void cgen_callinter(Node*, Node*, int); void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_dcl(Node*); -int cgen_inline(Node*, Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); /* * cgen */ void agen(Node*, Node*); -Prog* cgenindex(Node *, Node *); +Prog* cgenindex(Node *, Node *, int); void igen(Node*, Node*, Node*); void agenr(Node *n, Node *a, Node *res); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); @@ -104,12 +106,14 @@ Prog* gshift(int as, Node *lhs, int32 stype, int32 sval, Node *rhs); Prog * gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); +int componentgen(Node*, Node*); /* * cgen64.c */ -void cmp64(Node*, Node*, int, Prog*); +void cmp64(Node*, Node*, int, int, Prog*); void cgen64(Node*, Node*); /* @@ -117,16 +121,13 @@ void cgen64(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); void fnparam(Type*, int, int); Prog* gop(int, Node*, Node*, Node*); -void setconst(Addr*, vlong); -void setaddr(Addr*, Node*); int optoas(int, Type*); void ginit(void); void gclean(void); @@ -141,11 +142,12 @@ int isfat(Type*); int dotaddable(Node*, Node*); void sudoclean(void); int sudoaddable(int, Node*, Addr*, int*); -void afunclit(Addr*); +void afunclit(Addr*, Node*); void datagostring(Strlit*, Addr*); void split64(Node*, Node*, Node*); void splitclean(void); Node* ncon(uint32 i); +void gtrack(Sym*); /* * obj.c @@ -164,7 +166,7 @@ int Rconv(Fmt*); int Yconv(Fmt*); void listinit(void); -void zaddr(Biobuf*, Addr*, int); +void zaddr(Biobuf*, Addr*, int, int); #pragma varargck type "D" Addr* #pragma varargck type "M" Addr* diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index de100620b..de1671bb6 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -28,6 +28,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.name == D_AUTO && p->from.node) p->from.node->used = 1; @@ -40,27 +43,38 @@ markautoused(Prog* p) void fixautoused(Prog* p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.name == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } + if (p->from.name == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.name == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; - Node n1, r, con; + Node n1, r, r1, con; switch(proc) { default: @@ -68,8 +82,26 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ABL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ABL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(&r, types[tptr], 7); + nodreg(&r1, types[tptr], 1); + gmove(f, &r); + r.op = OINDREG; + gmove(&r, &r1); + r.op = OREGISTER; + r1.op = OINDREG; + gins(ABL, &r, &r1); + break; + + case 3: // normal call of c function pointer + gins(ABL, N, f); break; case 1: // call in new proc (go) @@ -120,7 +152,7 @@ ginscall(Node *f, int proc) nodconst(&con, types[TINT32], 0); p = gins(ACMP, &con, N); p->reg = 0; - patch(gbranch(ABNE, T), retpc); + patch(gbranch(ABNE, T, -1), retpc); } break; } @@ -136,6 +168,7 @@ cgen_callinter(Node *n, Node *res, int proc) int r; Node *i, *f; Node tmpi, nodo, nodr, nodsp; + Prog *p; i = n->left; if(i->op != ODOTINTER) @@ -180,7 +213,17 @@ cgen_callinter(Node *n, Node *res, int proc) cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + nodr.op = OINDREG; + proc = 3; + } else { + // go/defer. generate go func value. + p = gins(AMOVW, &nodo, &nodr); + p->from.type = D_CONST; // REG = &(20+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -368,14 +411,19 @@ cgen_asop(Node *n) case OOR: a = optoas(n->etype, nl->type); if(nl->addable) { - regalloc(&n3, nr->type, N); - cgen(nr, &n3); + if(smallintconst(nr)) + n3 = *nr; + else { + regalloc(&n3, nr->type, N); + cgen(nr, &n3); + } regalloc(&n2, nl->type, N); cgen(nl, &n2); gins(a, &n3, &n2); cgen(&n2, nl); regfree(&n2); - regfree(&n3); + if(n3.op != OLITERAL) + regfree(&n3); goto ret; } if(nr->ullman < UINF) @@ -399,7 +447,9 @@ cgen_asop(Node *n) hard: n2.op = 0; n1.op = 0; - if(nr->ullman >= nl->ullman || nl->addable) { + if(nr->op == OLITERAL) { + // don't allocate a register for literals. + } else if(nr->ullman >= nl->ullman || nl->addable) { regalloc(&n2, nr->type, N); cgen(nr, &n2); nr = &n2; @@ -464,24 +514,99 @@ samereg(Node *a, Node *b) } /* + * generate high multiply + * res = (nl * nr) >> wordsize + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) +{ + int w; + Node n1, n2, *tmp; + Type *t; + Prog *p; + + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + t = nl->type; + w = t->width * 8; + regalloc(&n1, t, res); + cgen(nl, &n1); + regalloc(&n2, t, N); + cgen(nr, &n2); + switch(simtype[t->etype]) { + case TINT8: + case TINT16: + gins(optoas(OMUL, t), &n2, &n1); + gshift(AMOVW, &n1, SHIFT_AR, w, &n1); + break; + case TUINT8: + case TUINT16: + gins(optoas(OMUL, t), &n2, &n1); + gshift(AMOVW, &n1, SHIFT_LR, w, &n1); + break; + case TINT32: + case TUINT32: + // perform a long multiplication. + if(issigned[t->etype]) + p = gins(AMULL, &n2, N); + else + p = gins(AMULLU, &n2, N); + // n2 * n1 -> (n1 n2) + p->reg = n1.val.u.reg; + p->to.type = D_REGREG; + p->to.reg = n1.val.u.reg; + p->to.offset = n2.val.u.reg; + break; + default: + fatal("cgen_hmul %T", t); + break; + } + cgen(&n1, res); + regfree(&n1); + regfree(&n2); +} + +/* * generate shift according to op, one of: * res = nl << nr * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, n3, nt, t, lo, hi; - int w; + int w, v; Prog *p1, *p2, *p3; Type *tr; uvlong sc; + USED(bounded); if(nl->type->width > 4) fatal("cgen_shift %T", nl->type); w = nl->type->width * 8; + if(op == OLROT) { + v = mpgetfix(nr->val.u.xval); + regalloc(&n1, nl->type, res); + if(w == 32) { + cgen(nl, &n1); + gshift(AMOVW, &n1, SHIFT_RR, w-v, &n1); + } else { + regalloc(&n2, nl->type, N); + cgen(nl, &n2); + gshift(AMOVW, &n2, SHIFT_LL, v, &n1); + gshift(AORR, &n2, SHIFT_LR, w-v, &n1); + regfree(&n2); + } + gmove(&n1, res); + regfree(&n1); + return; + } + if(nr->op == OLITERAL) { regalloc(&n1, nl->type, res); cgen(nl, &n1); @@ -524,6 +649,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) regalloc(&n3, types[TUINT32], N); gmove(&lo, &n1); gmove(&hi, &n3); + splitclean(); gins(ATST, &n3, N); nodconst(&t, types[TUINT32], w); p1 = gins(AMOVW, &t, &n1); @@ -546,9 +672,10 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) // test for shift being 0 gins(ATST, &n1, N); - p3 = gbranch(ABEQ, T); + p3 = gbranch(ABEQ, T, -1); // test and fix up large shifts + // TODO: if(!bounded), don't emit some of this. regalloc(&n3, tr, N); nodconst(&t, types[TUINT32], w); gmove(&t, &n3); @@ -589,7 +716,12 @@ clearfat(Node *nl) if(debug['g']) dump("\nclearfat", nl); + w = nl->type->width; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; + c = w % 4; // bytes q = w / 4; // quads @@ -613,7 +745,7 @@ clearfat(Node *nl) p = gins(ACMP, &dst, N); raddr(&end, p); - patch(gbranch(ABNE, T), pl); + patch(gbranch(ABNE, T, 0), pl); regfree(&end); } else @@ -637,395 +769,3 @@ clearfat(Node *nl) regfree(&dst); regfree(&nz); } - -static int -regcmp(const void *va, const void *vb) -{ - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} - -static Prog* throwpc; - -// We're only going to bother inlining if we can -// convert all the arguments to 32 bits safely. Can we? -static int -fix64(NodeList *nn, int n) -{ - NodeList *l; - Node *r; - int i; - - l = nn; - for(i=0; i<n; i++) { - r = l->n->right; - if(is64(r->type) && !smallintconst(r)) { - if(r->op == OCONV) - r = r->left; - if(is64(r->type)) - return 0; - } - l = l->next; - } - return 1; -} - -void -getargs(NodeList *nn, Node *reg, int n) -{ - NodeList *l; - int i; - - throwpc = nil; - - l = nn; - for(i=0; i<n; i++) { - if(!smallintconst(l->n->right) && !isslice(l->n->right->type)) { - regalloc(reg+i, l->n->right->type, N); - cgen(l->n->right, reg+i); - } else - reg[i] = *l->n->right; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; - } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; i<n; i++) - reg[i].local = 0; -} - -void -cmpandthrow(Node *nl, Node *nr) -{ - vlong cl; - Prog *p1; - int op; - Node *c, n1, n2; - - op = OLE; - if(smallintconst(nl)) { - cl = mpgetfix(nl->val.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - - n1.op = OXXX; - if(nr->op != OREGISTER) { - regalloc(&n1, types[TUINT32], N); - gmove(nr, &n1); - nr = &n1; - } - n2.op = OXXX; - if(nl->op != OREGISTER) { - regalloc(&n2, types[TUINT32], N); - gmove(nl, &n2); - nl = &n2; - } - gcmp(optoas(OCMP, types[TUINT32]), nl, nr); - if(nr == &n1) - regfree(&n1); - if(nl == &n2) - regfree(&n2); - if(throwpc == nil) { - p1 = gbranch(optoas(op, types[TUINT32]), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); - } else { - op = brcom(op); - p1 = gbranch(optoas(op, types[TUINT32]), T); - patch(p1, throwpc); - } -} - -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; -} - -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) -{ - Node nodes[5]; - Node n1, n2, n3, nres, ntemp; - vlong v; - int i, narg; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - if(!fix64(n->list, 5)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - regalloc(&n1, types[TUINT32], N); - gins(AMOVB, &n2, &n1); - regfree(&n1); - } - - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.type = types[tptr]; - n2.xoffset += Array_array; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } - } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) { - regalloc(&n3, types[tptr], N); - gmove(&nodes[4], &n3); - gins(optoas(OMUL, types[tptr]), &n3, &n1); - regfree(&n3); - } - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - regfree(&n1); - } - gmove(&nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; - -sliceslice: - if(!fix64(n->list, narg)) - goto no; - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); - - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; - } - - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - regalloc(&n1, types[TUINT32], N); - gmove(&n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - gmove(&n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - // if(hb[2] > old.cap[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - cmpandthrow(&nodes[2], &n2); - - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_nel; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gmove(&n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gmove(&n1, &n2); - regfree(&n1); - } - } - - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.type = types[TUINT32]; - n2.xoffset += Array_cap; - gmove(&n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.type = types[tptr]; - n2.xoffset += Array_array; - regalloc(&n3, types[tptr], N); - gmove(&n2, &n3); - - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gmove(&n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n3, &n1); - } - } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) { - regalloc(&n2, types[tptr], N); - gmove(&nodes[3], &n2); - gins(optoas(OMUL, types[tptr]), &n2, &n1); - regfree(&n2); - } - gins(optoas(OADD, types[tptr]), &n3, &n1); - } - regfree(&n3); - - n2 = nres; - n2.type = types[tptr]; - n2.xoffset += Array_array; - gmove(&n1, &n2); - regfree(&n1); - - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - - if(!sleasy(res)) { - cgen(&nres, res); - } - return 1; - -no: - return 0; -} diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index 2763e7b16..9c5fb2a96 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -65,17 +65,17 @@ zhist(Biobuf *b, int line, vlong offset) Bputc(b, line>>8); Bputc(b, line>>16); Bputc(b, line>>24); - zaddr(b, &zprog.from, 0); + zaddr(b, &zprog.from, 0, 0); a = zprog.to; if(offset != 0) { a.offset = offset; a.type = D_CONST; } - zaddr(b, &a, 0); + zaddr(b, &a, 0, 0); } void -zaddr(Biobuf *b, Addr *a, int s) +zaddr(Biobuf *b, Addr *a, int s, int gotype) { int32 l; uint64 e; @@ -95,6 +95,7 @@ zaddr(Biobuf *b, Addr *a, int s) Bputc(b, a->reg); Bputc(b, s); Bputc(b, a->name); + Bputc(b, gotype); } switch(a->type) { @@ -128,9 +129,9 @@ zaddr(Biobuf *b, Addr *a, int s) break; case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; l = a->offset; Bputc(b, l); Bputc(b, l>>8); @@ -139,7 +140,7 @@ zaddr(Biobuf *b, Addr *a, int s) break; case D_SCONST: - n = a->sval; + n = a->u.sval; for(i=0; i<NSNAME; i++) { Bputc(b, *n); n++; @@ -147,11 +148,12 @@ zaddr(Biobuf *b, Addr *a, int s) break; case D_REGREG: + case D_REGREG2: Bputc(b, a->offset); break; case D_FCONST: - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -166,20 +168,66 @@ zaddr(Biobuf *b, Addr *a, int s) } } +static struct { + struct { Sym *sym; short type; } h[NSYM]; + int sym; +} z; + +static void +zsymreset(void) +{ + for(z.sym=0; z.sym<NSYM; z.sym++) { + z.h[z.sym].sym = S; + z.h[z.sym].type = 0; + } + z.sym = 1; +} + +static int +zsym(Sym *s, int t, int *new) +{ + int i; + + *new = 0; + if(s == S) + return 0; + + i = s->sym; + if(i < 0 || i >= NSYM) + i = 0; + if(z.h[i].type == t && z.h[i].sym == s) + return i; + i = z.sym; + s->sym = i; + zname(bout, s, t); + z.h[i].sym = s; + z.h[i].type = t; + if(++z.sym >= NSYM) + z.sym = 1; + *new = 1; + return i; +} + +static int +zsymaddr(Addr *a, int *new) +{ + int t; + + t = a->name; + if(t == D_ADDR) + t = a->name; + return zsym(a->sym, t, new); +} + void dumpfuncs(void) { Plist *pl; - int sf, st, t, sym; - struct { Sym *sym; short type; } h[NSYM]; + int sf, st, gf, gt, new; Sym *s; Prog *p; - for(sym=0; sym<NSYM; sym++) { - h[sym].sym = S; - h[sym].type = 0; - } - sym = 1; + zsymreset(); // fix up pc pcloc = 0; @@ -209,53 +257,20 @@ dumpfuncs(void) } for(p=pl->firstpc; p!=P; p=p->link) { - jackpot: - sf = 0; - s = p->from.sym; - while(s != S) { - sf = s->sym; - if(sf < 0 || sf >= NSYM) - sf = 0; - t = p->from.name; - if(t == D_ADDR) - t = p->from.name; - if(h[sf].type == t) - if(h[sf].sym == s) - break; - s->sym = sym; - zname(bout, s, t); - h[sym].sym = s; - h[sym].type = t; - sf = sym; - sym++; - if(sym >= NSYM) - sym = 1; - break; - } - st = 0; - s = p->to.sym; - while(s != S) { - st = s->sym; - if(st < 0 || st >= NSYM) - st = 0; - t = p->to.name; - if(t == D_ADDR) - t = p->to.name; - if(h[st].type == t) - if(h[st].sym == s) - break; - s->sym = sym; - zname(bout, s, t); - h[sym].sym = s; - h[sym].type = t; - st = sym; - sym++; - if(sym >= NSYM) - sym = 1; - if(st == sf) - goto jackpot; + for(;;) { + sf = zsymaddr(&p->from, &new); + gf = zsym(p->from.gotype, D_EXTERN, &new); + if(new && sf == gf) + continue; + st = zsymaddr(&p->to, &new); + if(new && (st == sf || st == gf)) + continue; + gt = zsym(p->to.gotype, D_EXTERN, &new); + if(new && (gt == sf || gt == gf || gt == st)) + continue; break; } + Bputc(bout, p->as); Bputc(bout, p->scond); Bputc(bout, p->reg); @@ -263,8 +278,8 @@ dumpfuncs(void) Bputc(bout, p->lineno>>8); Bputc(bout, p->lineno>>16); Bputc(bout, p->lineno>>24); - zaddr(bout, &p->from, sf); - zaddr(bout, &p->to, st); + zaddr(bout, &p->from, sf, gf); + zaddr(bout, &p->to, st, gt); } } } @@ -288,7 +303,7 @@ dsname(Sym *sym, int off, char *t, int n) p->to.name = D_NONE; p->to.reg = NREG; p->to.offset = 0; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -372,13 +387,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->reg = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->reg = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 9acf93670..191c755b8 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -108,17 +108,22 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; USED(t); + USED(likely); // TODO: record this for linker p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; return p; } @@ -130,7 +135,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -141,8 +146,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -170,64 +175,6 @@ newplist(void) } void -clearstk(void) -{ - Plist *pl; - Prog *p, *p1, *p2, *p3; - Node dst, end, zero, con; - - if(plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - - // MOVW $4(SP), R1 - nodreg(&dst, types[tptr], 1); - p = gins(AMOVW, N, &dst); - p->from.type = D_CONST; - p->from.reg = REGSP; - p->from.offset = 4; - - // MOVW $n(R1), R2 - nodreg(&end, types[tptr], 2); - p = gins(AMOVW, N, &end); - p->from.type = D_CONST; - p->from.reg = 1; - p->from.offset = p1->to.offset; - - // MOVW $0, R3 - nodreg(&zero, types[TUINT32], 3); - nodconst(&con, types[TUINT32], 0); - gmove(&con, &zero); - - // L: - // MOVW.P R3, 0(R1) +4 - // CMP R1, R2 - // BNE L - p = gins(AMOVW, &zero, &dst); - p->to.type = D_OREG; - p->to.offset = 4; - p->scond |= C_PBIT; - p3 = p; - p = gins(ACMP, &dst, N); - raddr(&end, p); - patch(gbranch(ABNE, T), p3); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - -void gused(Node *n) { gins(ANOP, n, N); // used @@ -238,22 +185,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AB, T); + p = gbranch(AB, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->reg = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -261,7 +209,7 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -273,8 +221,20 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.name = D_NONE; p->to.offset = width; if(dupok) - p->reg = DUPOK; - p->reg |= RODATA; + p->reg |= DUPOK; + if(rodata) + p->reg |= RODATA; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = s; } int @@ -298,17 +258,20 @@ isfat(Type *t) * also fix up direct register references to be D_OREG. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_CONST && a->name == D_EXTERN || a->type == D_REG) { a->type = D_OREG; + if(n->op == ONAME) + a->sym = n->sym; } } static int resvd[] = { - 9, // reserved for m - 10, // reserved for g + 9, // reserved for m + 10, // reserved for g + REGSP, // reserved for SP }; void @@ -401,6 +364,7 @@ regalloc(Node *n, Type *t, Node *o) regpc[i] = (uintptr)getcallerpc(&n); goto out; } + print("registers allocated at\n"); for(i=REGALLOC_R0; i<=REGALLOC_RMAX; i++) print("%d %p\n", i, regpc[i]); yyerror("out of fixed registers"); @@ -452,15 +416,17 @@ regfree(Node *n) print("regalloc fix %d float %d\n", fixfree, floatfree); } - if(n->op == ONAME && iscomplex[n->type->etype]) + if(n->op == ONAME) return; if(n->op != OREGISTER && n->op != OINDREG) fatal("regfree: not a register"); i = n->val.u.reg; + if(i == REGSP) + return; if(i < 0 || i >= nelem(reg) || i >= nelem(regpc)) fatal("regfree: reg out of range"); if(reg[i] <= 0) - fatal("regfree: reg not allocated"); + fatal("regfree: reg %R not allocated", i); reg[i]--; if(reg[i] == 0) regpc[i] = 0; @@ -597,9 +563,9 @@ split64(Node *n, Node *lo, Node *hi) if(!is64(n->type)) fatal("split64 %T", n->type); - sclean[nsclean].op = OEMPTY; if(nsclean >= nelem(sclean)) fatal("split64 clean"); + sclean[nsclean].op = OEMPTY; nsclean++; switch(n->op) { default: @@ -1155,7 +1121,7 @@ gcmp(int as, Node *lhs, Node *rhs) { Prog *p; - if(lhs->op != OREGISTER || rhs->op != OREGISTER) + if(lhs->op != OREGISTER) fatal("bad operands to gcmp: %O %O", lhs->op, rhs->op); p = gins(as, rhs, N); @@ -1194,6 +1160,27 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs) return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m1, m2; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m1, types[TUINTPTR], n); + regalloc(&m2, types[TUINT8], n); + cgen(n, &m1); + m1.xoffset = 0; + m1.op = OINDREG; + m1.type = types[TUINT8]; + gins(AMOVBU, &m1, &m2); + regfree(&m2); + regfree(&m1); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1209,7 +1196,7 @@ checkoffset(Addr *a, int canemitcode) // reference with large offset. instead, emit explicit // test of 0(reg). regalloc(&n1, types[TUINTPTR], N); - p = gins(AMOVW, N, &n1); + p = gins(AMOVB, N, &n1); p->from = *a; p->from.offset = 0; regfree(&n1); @@ -1222,14 +1209,22 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->type = D_NONE; a->name = D_NONE; a->reg = NREG; + a->gotype = S; a->node = N; a->etype = 0; if(n == N) return; + if(n->type != T && n->type->etype != TIDEAL) { + dowidth(n->type); + a->width = n->type->width; + } + switch(n->op) { default: fatal("naddr: bad %O %D", n->op, a); @@ -1286,6 +1281,25 @@ naddr(Node *n, Addr *a, int canemitcode) a->name = D_PARAM; a->node = n->left->orig; break; + + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVW, N, N); + p->from.type = D_OREG; + p->from.reg = 7; + p->from.offset = n->xoffset; + p->to.type = D_REG; + p->to.reg = 1; + a->type = D_REG; + a->reg = 1; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; case ONAME: a->etype = 0; @@ -1326,6 +1340,7 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->name = D_EXTERN; a->type = D_CONST; + a->sym = funcsym(a->sym); break; } break; @@ -1337,7 +1352,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: @@ -1409,6 +1424,9 @@ naddr(Node *n, Addr *a, int canemitcode) fatal("naddr: OADDR %d\n", a->type); } } + + if(a->width < 0) + fatal("naddr: bad width for %N -> %D", n, a); } /* @@ -1619,6 +1637,16 @@ optoas(int op, Type *t) a = ASUBD; break; + case CASE(OMINUS, TINT8): + case CASE(OMINUS, TUINT8): + case CASE(OMINUS, TINT16): + case CASE(OMINUS, TUINT16): + case CASE(OMINUS, TINT32): + case CASE(OMINUS, TUINT32): + case CASE(OMINUS, TPTR32): + a = ARSB; + break; + case CASE(OAND, TINT8): case CASE(OAND, TUINT8): case CASE(OAND, TINT16): @@ -1825,6 +1853,9 @@ sudoaddable(int as, Node *n, Addr *a, int *w) goto odot; case OINDEX: + return 0; + // disabled: OINDEX case is now covered by agenr + // for a more suitable register allocation pattern. if(n->left->type->etype == TSTRING) return 0; cleani += 2; @@ -1942,7 +1973,7 @@ oindex: t = types[TINT32]; regalloc(reg1, t, N); regalloc(&n3, types[TINT32], reg1); - p2 = cgenindex(r, &n3); + p2 = cgenindex(r, &n3, debug['B'] || n->bounded); gmove(&n3, reg1); regfree(&n3); @@ -1982,7 +2013,7 @@ oindex: cgen(&n2, &n3); gcmp(optoas(OCMP, types[TUINT32]), reg1, &n3); regfree(&n3); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p2) patch(p2, pc); ginscall(panicindex, 0); @@ -2032,7 +2063,7 @@ oindex_const: v = mpgetfix(r->val.u.xval); if(o & ODynam) { - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; @@ -2045,7 +2076,7 @@ oindex_const: gcmp(optoas(OCMP, types[TUINT32]), &n4, &n3); regfree(&n4); regfree(&n3); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); ginscall(panicindex, 0); patch(p1, pc); } diff --git a/src/cmd/5g/list.c b/src/cmd/5g/list.c index f0da24742..6c3f1d744 100644 --- a/src/cmd/5g/list.c +++ b/src/cmd/5g/list.c @@ -153,6 +153,12 @@ Dconv(Fmt *fp) sprint(str, "%M(R%d)(REG)", a, a->reg); break; + case D_REGREG2: + sprint(str, "R%d,R%d", a->reg, (int)a->offset); + if(a->name != D_NONE || a->sym != S) + sprint(str, "%M(R%d)(REG)", a, a->reg); + break; + case D_FREG: sprint(str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) @@ -160,24 +166,24 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->branch == P || a->branch->loc == 0) { + if(a->u.branch == P || a->u.branch->loc == 0) { if(a->sym != S) sprint(str, "%s+%d(APC)", a->sym->name, a->offset); else sprint(str, "%d(APC)", a->offset); } else if(a->sym != S) - sprint(str, "%s+%d(APC)", a->sym->name, a->branch->loc); + sprint(str, "%s+%d(APC)", a->sym->name, a->u.branch->loc); else - sprint(str, "%d(APC)", a->branch->loc); + sprint(str, "%d(APC)", a->u.branch->loc); break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; // TODO(kaib): Add back @@ -190,7 +196,10 @@ Dconv(Fmt *fp) // goto conv; } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } int diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h index 7a0070fc9..af7d654de 100644 --- a/src/cmd/5g/opt.h +++ b/src/cmd/5g/opt.h @@ -34,8 +34,6 @@ #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - #define BLOAD(r) band(bnot(r->refbehind), r->refahead) #define BSTORE(r) band(bnot(r->calbehind), r->calahead) #define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z]) @@ -49,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -70,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index e87f5d697..b6202a882 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -1,5 +1,5 @@ // Inferno utils/5c/peep.c -// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/peep.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -49,7 +49,6 @@ peep(void) int t; p1 = nil; - USED(p1); // ... in unreachable code... /* * complete R structure */ @@ -77,6 +76,8 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } @@ -120,7 +121,7 @@ loop1: } break; -#ifdef NOTDEF +#ifdef NOTDEF if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) if(isdconst(&p->from)) { @@ -133,26 +134,24 @@ loop1: if(t) goto loop1; -return; -#ifdef NOTDEF for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { -// case AEOR: -// /* -// * EOR -1,x,y => MVN x,y -// */ -// if(isdconst(&p->from) && p->from.offset == -1) { -// p->as = AMVN; -// p->from.type = D_REG; -// if(p->reg != NREG) -// p->from.reg = p->reg; -// else -// p->from.reg = p->to.reg; -// p->reg = NREG; -// } -// break; + case AEOR: + /* + * EOR -1,x,y => MVN x,y + */ + if(isdconst(&p->from) && p->from.offset == -1) { + p->as = AMVN; + p->from.type = D_REG; + if(p->reg != NREG) + p->from.reg = p->reg; + else + p->from.reg = p->to.reg; + p->reg = NREG; + } + break; case AMOVH: case AMOVHU: @@ -161,6 +160,7 @@ return; /* * look for MOVB x,R; MOVB R,R */ + r1 = r->link; if(p->to.type != D_REG) break; if(r1 == R) @@ -175,23 +175,22 @@ return; excise(r1); break; } - r1 = r->link; } -// for(r=firstr; r!=R; r=r->link) { -// p = r->prog; -// switch(p->as) { -// case AMOVW: -// case AMOVB: -// case AMOVBU: -// if(p->from.type == D_OREG && p->from.offset == 0) -// xtramodes(r, &p->from); -// else -// if(p->to.type == D_OREG && p->to.offset == 0) -// xtramodes(r, &p->to); -// else -// continue; -// break; + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVW: + case AMOVB: + case AMOVBU: + if(p->from.type == D_OREG && p->from.offset == 0) + xtramodes(r, &p->from); + else + if(p->to.type == D_OREG && p->to.offset == 0) + xtramodes(r, &p->to); + else + continue; + break; // case ACMP: // /* // * elide CMP $0,x if calculation of x can set condition codes @@ -259,13 +258,17 @@ return; // r2->prog->as = t; // excise(r); // continue; -// } -// } + } + } - predicate(); -#endif +// predicate(); } +/* + * uniqp returns a "unique" predecessor to instruction r. + * If the instruction is the first one or has multiple + * predecessors due to jump, R is returned. + */ Reg* uniqp(Reg *r) { @@ -738,6 +741,11 @@ shiftprop(Reg *r) return 1; } +/* + * findpre returns the last instruction mentioning v + * before r. It must be a set, and there must be + * a unique path from that instruction to r. + */ Reg* findpre(Reg *r, Adr *v) { @@ -758,6 +766,10 @@ findpre(Reg *r, Adr *v) return R; } +/* + * findinc finds ADD instructions with a constant + * argument which falls within the immed_12 range. + */ Reg* findinc(Reg *r, Reg *r2, Adr *v) { @@ -848,6 +860,19 @@ finduse(Reg *r, Adr *v) return findu1(r, v); } +/* + * xtramodes enables the ARM post increment and + * shift offset addressing modes to transform + * MOVW 0(R3),R1 + * ADD $4,R3,R3 + * into + * MOVW.P 4(R3),R1 + * and + * ADD R0,R1 + * MOVBU 0(R1),R0 + * into + * MOVBU R0<<0(R1),R0 + */ int xtramodes(Reg *r, Adr *a) { @@ -856,8 +881,6 @@ xtramodes(Reg *r, Adr *a) Adr v; p = r->prog; - if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ - return 0; v = *a; v.type = D_REG; r1 = findpre(r, &v); @@ -866,6 +889,9 @@ xtramodes(Reg *r, Adr *a) if(p1->to.type == D_REG && p1->to.reg == v.reg) switch(p1->as) { case AADD: + if(p1->scond & C_SBIT) + // avoid altering ADD.S/ADC sequences. + break; if(p1->from.type == D_REG || (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || @@ -1032,6 +1058,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case AMULLU: /* read, read, write, write */ + case AMULL: case AMULA: case AMVN: return 2; @@ -1135,12 +1162,9 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == D_REG) - if(v->reg == REGRET) - return 2; - if(v->type == D_FREG) - if(v->reg == FREGRET) - return 2; + if(s != A) + return 1; + return 3; case ABL: /* funny */ if(v->type == D_REG) { @@ -1152,6 +1176,8 @@ copyu(Prog *p, Adr *v, Adr *s) if(v->type == D_FREG) if(v->reg <= FREGEXT && v->reg > exfregoffset) return 2; + if(p->from.type == D_REG && v->type == D_REG && p->from.reg == v->reg) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -1167,6 +1193,9 @@ copyu(Prog *p, Adr *v, Adr *s) if(v->reg == (uchar)REGARG) return 3; return 0; + + case ALOCALS: /* funny */ + return 0; } } @@ -1213,7 +1242,7 @@ copyau(Adr *a, Adr *v) if(a->reg == v->reg) return 1; } else - if(a->type == D_REGREG) { + if(a->type == D_REGREG || a->type == D_REGREG2) { if(a->reg == v->reg) return 1; if(a->offset == v->reg) @@ -1276,7 +1305,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f) if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); } else - if(a->type == D_REGREG) { + if(a->type == D_REGREG || a->type == D_REGREG2) { if(a->offset == v->reg) a->offset = s->reg; if(a->reg == v->reg) diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 393266973..eaaaf9be3 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -1,5 +1,5 @@ // Inferno utils/5c/reg.c -// http://code.google.com/p/inferno-os/source/browse/utils/5g/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/5c/reg.c // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) @@ -34,8 +34,8 @@ #include "gg.h" #include "opt.h" -#define NREGVAR 24 -#define REGBITS ((uint32)0xffffff) +#define NREGVAR 32 +#define REGBITS ((uint32)0xffffffff) #define P2R(p) (Reg*)(p->reg) void addsplits(void); @@ -95,7 +95,7 @@ setoutvar(void) ovar.b[z] |= bit.b[z]; t = structnext(&save); } -//if(bany(ovar)) +//if(bany(&ovar)) //print("ovar = %Q\n", ovar); } @@ -160,8 +160,18 @@ static char* regname[] = { ".F5", ".F6", ".F7", + ".F8", + ".F9", + ".F10", + ".F11", + ".F12", + ".F13", + ".F14", + ".F15", }; +static Node* regnodes[NREGVAR]; + void regopt(Prog *firstp) { @@ -197,7 +207,6 @@ regopt(Prog *firstp) return; } - r1 = R; firstr = R; lastr = R; @@ -208,8 +217,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) - var[i].node = newname(lookup(regname[i])); + for(i=0; i<NREGVAR; i++) { + if(regnodes[i] == N) + regnodes[i] = newname(lookup(regname[i])); + var[i].node = regnodes[i]; + } regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { @@ -236,6 +248,8 @@ regopt(Prog *firstp) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: continue; } r = rega(); @@ -263,6 +277,10 @@ regopt(Prog *firstp) } } + // Avoid making variables for direct-called functions. + if(p->as == ABL && p->to.type == D_EXTERN) + continue; + /* * left side always read */ @@ -408,10 +426,14 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } + if(debug['R'] && debug['v']) + dumpit("pass1", firstr); + /* * pass 2 * turn branch references to pointers @@ -420,9 +442,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->regp; + r1 = p->to.u.branch->regp; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -440,6 +462,9 @@ regopt(Prog *firstp) print(" addr = %Q\n", addrs); } + if(debug['R'] && debug['v']) + dumpit("pass2", firstr); + /* * pass 2.5 * find looping structure @@ -449,6 +474,9 @@ regopt(Prog *firstp) change = 0; loopit(firstr, nr); + if(debug['R'] && debug['v']) + dumpit("pass2.5", firstr); + /* * pass 3 * iterate propagating usage @@ -476,6 +504,9 @@ loop11: if(change) goto loop1; + if(debug['R'] && debug['v']) + dumpit("pass3", firstr); + /* * pass 4 @@ -492,6 +523,9 @@ loop2: addsplits(); + if(debug['R'] && debug['v']) + dumpit("pass4", firstr); + if(debug['R'] > 1) { print("\nprop structure:\n"); for(r = firstr; r != R; r = r->link) { @@ -543,6 +577,9 @@ loop2: r->act.b[0] &= ~REGBITS; } + if(debug['R'] && debug['v']) + dumpit("pass4.5", firstr); + /* * pass 5 * isolate regions @@ -605,6 +642,9 @@ loop2: brk: qsort(region, nregion, sizeof(region[0]), rcmp); + if(debug['R'] && debug['v']) + dumpit("pass5", firstr); + /* * pass 6 * determine used registers (paint2) @@ -633,6 +673,10 @@ brk: paint3(rgp->enter, rgp->varno, vreg, rgp->regno); rgp++; } + + if(debug['R'] && debug['v']) + dumpit("pass6", firstr); + /* * pass 7 * peep-hole on basic block @@ -641,6 +685,9 @@ brk: peep(); } + if(debug['R'] && debug['v']) + dumpit("pass7", firstr); + /* * last pass * eliminate nops @@ -662,8 +709,8 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; if(p->as == AMOVW && p->to.reg == 13) { if(p->scond & C_WBIT) { vreg = -p->to.offset; // in adjust region @@ -866,6 +913,7 @@ mkvar(Reg *r, Adr *a) goto onereg; case D_REGREG: + case D_REGREG2: bit = zbits; if(a->offset != NREG) bit.b[0] |= RtoB(a->offset); @@ -926,6 +974,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %d for %D", w, a); for(i=0; i<nvar; i++) { v = var+i; @@ -947,8 +997,6 @@ mkvar(Reg *r, Adr *a) switch(et) { case 0: case TFUNC: - case TARRAY: - case TSTRING: goto none; } @@ -964,14 +1012,13 @@ mkvar(Reg *r, Adr *a) v = var+i; v->offset = o; v->name = n; -// v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); bit = blsh(i); if(n == D_EXTERN || n == D_STATIC) @@ -1033,8 +1080,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; z<BITS; z++) + for(z=0; z<BITS; z++) { cal.b[z] |= externs.b[z]; + // issue 4066: flush modified return variables in case of panic + if(hasdefer) + cal.b[z] |= ovar.b[z]; + } break; } for(z=0; z<BITS; z++) { @@ -1486,11 +1537,12 @@ addreg(Adr *a, int rn) * 1 R1 * ... ... * 10 R10 + * 12 R12 */ int32 RtoB(int r) { - if(r >= REGTMP-2) // excluded R9 and R10 for m and g + if(r >= REGTMP-2 && r != 12) // excluded R9 and R10 for m and g, but not R12 return 0; return 1L << r; } @@ -1498,7 +1550,7 @@ RtoB(int r) int BtoR(int32 b) { - b &= 0x01fcL; // excluded R9 and R10 for m and g + b &= 0x11fcL; // excluded R9 and R10 for m and g, but not R12 if(b == 0) return 0; return bitno(b); @@ -1509,7 +1561,7 @@ BtoR(int32 b) * 18 F2 * 19 F3 * ... ... - * 23 F7 + * 31 F15 */ int32 FtoB(int f) @@ -1524,7 +1576,7 @@ int BtoF(int32 b) { - b &= 0xfc0000L; + b &= 0xfffc0000L; if(b == 0) return 0; return bitno(b) - 16; @@ -1643,7 +1695,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1665,8 +1717,8 @@ mark(Prog *firstp) if(p->regp != dead) break; p->regp = alive; - if(p->as != ABL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); + if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); if(p->as == AB || p->as == ARET || (p->as == ABL && noreturn(p))) break; } @@ -1686,8 +1738,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ABL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AB) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ABL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AB) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1695,7 +1747,7 @@ fixjmp(Prog *firstp) } if(debug['R'] && debug['v']) print("\n"); - + // pass 2: mark all reachable code alive mark(firstp); @@ -1723,7 +1775,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AB && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AB && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 569536ebd..4aef8a27f 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -49,12 +49,11 @@ #define REGM (REGEXT-1) /* compiler allocates external registers R10 down */ #define REGTMP 11 -#define REGSB 12 #define REGSP 13 #define REGLINK 14 #define REGPC 15 -#define NFREG 8 +#define NFREG 16 #define FREGRET 0 #define FREGEXT 7 #define FREGTMP 15 @@ -127,6 +126,8 @@ enum as ADIVD, ASQRTF, ASQRTD, + AABSF, + AABSD, ASRL, ASRA, @@ -184,6 +185,21 @@ enum as ALDREXD, ASTREXD, + APLD, + + AUNDEF, + + ACLZ, + + AMULWT, + AMULWB, + AMULAWT, + AMULAWB, + + AUSEFIELD, + ALOCALS, + ATYPE, + ALAST, }; @@ -237,12 +253,14 @@ enum as #define D_SHIFT (D_NONE+19) #define D_FPCR (D_NONE+20) -#define D_REGREG (D_NONE+21) +#define D_REGREG (D_NONE+21) // (reg, reg) #define D_ADDR (D_NONE+22) #define D_SBIG (D_NONE+23) #define D_CONST2 (D_NONE+24) +#define D_REGREG2 (D_NONE+25) // reg, reg + /* name */ #define D_EXTERN (D_NONE+3) #define D_STATIC (D_NONE+4) @@ -252,11 +270,16 @@ enum as /* internal only */ #define D_SIZE (D_NONE+40) #define D_PCREL (D_NONE+41) +#define D_GOTOFF (D_NONE+42) // R_ARM_GOTOFF +#define D_PLT0 (D_NONE+43) // R_ARM_PLT32, 1st inst: add ip, pc, #0xNN00000 +#define D_PLT1 (D_NONE+44) // R_ARM_PLT32, 2nd inst: add ip, ip, #0xNN000 +#define D_PLT2 (D_NONE+45) // R_ARM_PLT32, 3rd inst: ldr pc, [ip, #0xNNN]! +#define D_CALL (D_NONE+46) // R_ARM_PLT32/R_ARM_CALL/R_ARM_JUMP24, bl xxxxx or b yyyyy /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index b36a982d1..04f2a9c6c 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -33,10 +33,14 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" static Prog *PP; -char linuxdynld[] = "/lib/ld-linux.so.2"; +char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI +char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; +char openbsddynld[] = "XXX"; +char netbsddynld[] = "/libexec/ld.elf_so"; int32 entryvalue(void) @@ -55,32 +59,6 @@ entryvalue(void) return s->value; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRel, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrSymtab, - ElfStrStrtab, - ElfStrShstrtab, - ElfStrRelPlt, - ElfStrPlt, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -103,164 +81,437 @@ needlib(char *name) int nelfsym = 1; +static void addpltsym(Sym*); +static void addgotsym(Sym*); +static void addgotsyminternal(Sym*); + +// Preserve highest 8 bits of a, and do addition to lower 24-bit +// of a and b; used to adjust ARM branch intruction's target +static int32 +braddoff(int32 a, int32 b) +{ + return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); +} + +Sym * +lookuprel(void) +{ + return lookup(".rel", 0); +} + void -adddynrel(Sym *s, Reloc *r) +adddynrela(Sym *rel, Sym *s, Reloc *r) { - USED(s); - USED(r); - diag("adddynrel: unsupported binary format"); + addaddrplus(rel, s, r->off); + adduint32(rel, R_ARM_RELATIVE); } void -adddynsym(Sym *s) +adddynrel(Sym *s, Reloc *r) { - USED(s); - diag("adddynsym: not implemented"); + Sym *targ, *rel; + + targ = r->sym; + cursym = s; + + switch(r->type) { + default: + if(r->type >= 256) { + diag("unexpected relocation type %d", r->type); + return; + } + break; + + // Handle relocations found in ELF object files. + case 256 + R_ARM_PLT32: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_THM_PC22: // R_ARM_THM_CALL + diag("R_ARM_THM_CALL, are you using -marm?"); + errorexit(); + return; + + case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + r->add += targ->got; + return; + + case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P + if(targ->dynimpname == nil || targ->dynexport) { + addgotsyminternal(targ); + } else { + addgotsym(targ); + } + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += targ->got + 4; + return; + + case 256 + R_ARM_GOTOFF: // R_ARM_GOTOFF32 + r->type = D_GOTOFF; + return; + + case 256 + R_ARM_GOTPC: // R_ARM_BASE_PREL + r->type = D_PCREL; + r->sym = lookup(".got", 0); + r->add += 4; + return; + + case 256 + R_ARM_CALL: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + + case 256 + R_ARM_REL32: // R_ARM_REL32 + r->type = D_PCREL; + r->add += 4; + return; + + case 256 + R_ARM_ABS32: + if(targ->dynimpname != nil && !targ->dynexport) + diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); + r->type = D_ADDR; + return; + + case 256 + R_ARM_V4BX: + // we can just ignore this, because we are targeting ARM V5+ anyway + if(r->sym) { + // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it + r->sym->type = 0; + } + r->sym = S; + return; + + case 256 + R_ARM_PC24: + case 256 + R_ARM_JUMP24: + r->type = D_CALL; + if(targ->dynimpname != nil && !targ->dynexport) { + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = braddoff(r->add, targ->plt / 4); + } + return; + } + + // Handle references to ELF symbols from our own object files. + if(targ->dynimpname == nil || targ->dynexport) + return; + + switch(r->type) { + case D_PCREL: + addpltsym(targ); + r->sym = lookup(".plt", 0); + r->add = targ->plt; + return; + + case D_ADDR: + if(s->type != SDATA) + break; + if(iself) { + adddynsym(targ); + rel = lookup(".rel", 0); + addaddrplus(rel, s, r->off); + adduint32(rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a S + A dynmic reloc + r->type = D_CONST; // write r->add during relocsym + r->sym = S; + return; + } + break; + } + + cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } -static void +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + USED(add); // written to obj file by ../ld/data.c's reloc + + LPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + LPUT(R_ARM_ABS32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_ARM_REL32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + +void elfsetupplt(void) { - // TODO + Sym *plt, *got; + + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + if(plt->size == 0) { + // str lr, [sp, #-4]! + adduint32(plt, 0xe52de004); + // ldr lr, [pc, #4] + adduint32(plt, 0xe59fe004); + // add lr, pc, lr + adduint32(plt, 0xe08fe00e); + // ldr pc, [lr, #8]! + adduint32(plt, 0xe5bef008); + // .word &GLOBAL_OFFSET_TABLE[0] - . + addpcrelplus(plt, got, 4); + + // the first .plt entry requires 3 .plt.got entries + adduint32(got, 0); + adduint32(got, 0); + adduint32(got, 0); + } } int archreloc(Reloc *r, Sym *s, vlong *val) { - USED(r); - USED(s); - USED(val); - return -1; + switch(r->type) { + case D_CONST: + *val = r->add; + return 0; + case D_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); + return 0; + // The following three arch specific relocations are only for generation of + // Linux/ARM ELF's PLT entry (3 assembler instruction) + case D_PLT0: // add ip, pc, #0xXX00000 + if (symaddr(lookup(".got.plt", 0)) < symaddr(lookup(".plt", 0))) + diag(".got.plt should be placed after .plt section."); + *val = 0xe28fc600U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add) >> 20)); + return 0; + case D_PLT1: // add ip, ip, #0xYY000 + *val = 0xe28cca00U + + (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 4) >> 12)); + return 0; + case D_PLT2: // ldr pc, [ip, #0xZZZ]! + *val = 0xe5bcf000U + + (0xfff & (uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 8)); + return 0; + case D_CALL: // bl XXXXXX or b YYYYYY + *val = braddoff((0xff000000U & (uint32)r->add), + (0xffffff & (uint32) + ((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4))); + return 0; +} +return -1; } -void -adddynlib(char *lib) +static Reloc * +addpltreloc(Sym *plt, Sym *got, Sym *sym, int typ) { - Sym *s; +Reloc *r; + r = addrel(plt); + r->sym = got; + r->off = plt->size; + r->siz = 4; + r->type = typ; + r->add = sym->got - 8; + + plt->reachable = 1; + plt->size += 4; + symgrow(plt, plt->size); + + return r; +} + +static void +addpltsym(Sym *s) +{ + Sym *plt, *got, *rel; - if(!needlib(lib)) + if(s->plt >= 0) return; + + adddynsym(s); if(iself) { - s = lookup(".dynstr", 0); - if(s->size == 0) - addstring(s, ""); - elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + plt = lookup(".plt", 0); + got = lookup(".got.plt", 0); + rel = lookup(".rel.plt", 0); + if(plt->size == 0) + elfsetupplt(); + + // .got entry + s->got = got->size; + // In theory, all GOT should point to the first PLT entry, + // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's + // dynamic linker won't, so we'd better do it ourselves. + addaddrplus(got, plt, 0); + + // .plt entry, this depends on the .got entry + s->plt = plt->size; + addpltreloc(plt, got, s, D_PLT0); // add lr, pc, #0xXX00000 + addpltreloc(plt, got, s, D_PLT1); // add lr, lr, #0xYY000 + addpltreloc(plt, got, s, D_PLT2); // ldr pc, [lr, #0xZZZ]! + + // rel + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_JUMP_SLOT)); } else { - diag("adddynlib: unsupported binary format"); + diag("addpltsym: unsupported binary format"); + } +} + +static void +addgotsyminternal(Sym *s) +{ + Sym *got; + + if(s->got >= 0) + return; + + got = lookup(".got", 0); + s->got = got->size; + + addaddrplus(got, s, 0); + + if(iself) { + ; + } else { + diag("addgotsyminternal: unsupported binary format"); + } +} + +static void +addgotsym(Sym *s) +{ + Sym *got, *rel; + + if(s->got >= 0) + return; + + adddynsym(s); + got = lookup(".got", 0); + s->got = got->size; + adduint32(got, 0); + + if(iself) { + rel = lookup(".rel", 0); + addaddrplus(rel, got, s->got); + adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_GLOB_DAT)); + } else { + diag("addgotsym: unsupported binary format"); } } void -doelf(void) +adddynsym(Sym *s) { - Sym *s, *shstrtab, *dynstr; + Sym *d; + int t; + char *name; - if(!iself) + if(s->dynid >= 0) return; - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); + if(s->dynimpname == nil) { + s->dynimpname = s->name; + //diag("adddynsym: no dynamic name for %s", s->name); } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); - elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->value += ELF32SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->type = SELFROSECT; - s->reachable = 1; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - /* relocation table */ - s = lookup(".rel", 0); - s->reachable = 1; - s->type = SELFROSECT; + if(iself) { + s->dynid = nelfsym++; - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* got.plt */ - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; + d = lookup(".dynsym", 0); - s = lookup(".rel.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); - elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); - elfwritedynent(s, DT_RELENT, ELF32RELSIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_REL); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); - elfwritedynent(s, DT_DEBUG, 0); - elfwritedynent(s, DT_NULL, 0); + /* name */ + name = s->dynimpname; + if(name == nil) + name = s->name; + adduint32(d, addstring(lookup(".dynstr", 0), name)); + + /* value */ + if(s->type == SDYNIMPORT) + adduint32(d, 0); + else + addaddr(d, s); + + /* size */ + adduint32(d, 0); + + /* type */ + t = STB_GLOBAL << 4; + if(s->dynexport && (s->type&SMASK) == STEXT) + t |= STT_FUNC; + else + t |= STT_OBJECT; + adduint8(d, t); + adduint8(d, 0); + + /* shndx */ + if(!s->dynexport && s->dynimpname != nil) + adduint16(d, SHN_UNDEF); + else { + switch(s->type) { + default: + case STEXT: + t = 11; + break; + case SRODATA: + t = 12; + break; + case SDATA: + t = 13; + break; + case SBSS: + t = 14; + break; + } + adduint16(d, t); + } + } else { + diag("adddynsym: unsupported binary format"); + } +} + +void +adddynlib(char *lib) +{ + Sym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = lookup(".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else { + diag("adddynlib: unsupported binary format"); } } @@ -276,48 +527,26 @@ datoff(vlong addr) } void -shsym(Elf64_Shdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - -void asmb(void) { int32 t; - int a, dynsym; - uint32 fo, symo, startva, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + uint32 symo; Section *sect; - int o; + Sym *sym; + int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -330,19 +559,6 @@ asmb(void) cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); - if(iself) { - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; - } - /* output symbol table */ symsize = 0; lcsize = 0; @@ -365,28 +581,43 @@ asmb(void) case Hplan9x32: symo = HEADR+segtext.len+segdata.filelen; break; - case Hnetbsd: - symo = rnd(segdata.filelen, 4096); - break; ElfSym: symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } cseek(symo); - if(iself) { - if(debug['v']) - Bprint(&bso, "%5.2f elfsym\n", cputime()); - asmelfsym(); + switch(HEADTYPE) { + default: + if(iself) { + if(debug['v']) + Bprint(&bso, "%5.2f elfsym\n", cputime()); + asmelfsym(); + cflush(); + cwrite(elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); + } + break; + case Hplan9x32: + asmplan9sym(); cflush(); - cwrite(elfstrdat, elfstrsize); - // if(debug['v']) - // Bprint(&bso, "%5.2f dwarf\n", cputime()); - // dwarfemitdebugsections(); + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + break; } - cflush(); - } cursym = nil; @@ -395,6 +626,7 @@ asmb(void) Bflush(&bso); cseek(0L); switch(HEADTYPE) { + default: case Hnoheader: /* no header */ break; case Hrisc: /* aif for risc os */ @@ -409,7 +641,7 @@ asmb(void) - 8) / 4); /* BL - entry code */ lputl(0xef000011); /* SWI - exit code */ - lputl(textsize+HEADR); /* text size */ + lputl(segtext.filelen+HEADR); /* text size */ lputl(segdata.filelen); /* data size */ lputl(0); /* sym size */ @@ -429,7 +661,7 @@ asmb(void) break; case Hplan9x32: /* plan 9 */ lput(0x647); /* magic */ - lput(textsize); /* sizes */ + lput(segtext.filelen); /* sizes */ lput(segdata.filelen); lput(segdata.len - segdata.filelen); lput(symsize); /* nsyms */ @@ -437,16 +669,6 @@ asmb(void) lput(0L); lput(lcsize); break; - case Hnetbsd: /* boot for NetBSD */ - lput((143<<16)|0413); /* magic */ - lputl(rnd(HEADR+textsize, 4096)); - lputl(rnd(segdata.filelen, 4096)); - lputl(segdata.len - segdata.filelen); - lputl(symsize); /* nsyms */ - lputl(entryvalue()); /* va of entry */ - lputl(0L); - lputl(0L); - break; case Hixp1200: /* boot for IXP1200 */ break; case Hipaq: /* boot for ipaq */ @@ -456,216 +678,20 @@ asmb(void) lputl(0xe3300000); /* nop */ break; case Hlinux: - /* elf arm */ - eh = getElfEhdr(); - fo = HEADR; - startva = INITTEXT - fo; /* va of byte 0 of file */ - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) - interpreter = linuxdynld; - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32SYMSIZE; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRel]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".rel", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 4; - } - */ - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 4; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 4; - sh->entsize = 16; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - // dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - eh->ident[EI_CLASS] = ELFCLASS32; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - - eh->type = ET_EXEC; - eh->machine = EM_ARM; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - if(pph != nil) { - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - } - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + asmbelf(symo); break; } cflush(); if(debug['c']){ - print("textsize=%d\n", textsize); + print("textsize=%ulld\n", segtext.filelen); print("datsize=%ulld\n", segdata.filelen); print("bsssize=%ulld\n", segdata.len - segdata.filelen); print("symsize=%d\n", symsize); print("lcsize=%d\n", lcsize); - print("total=%lld\n", textsize+segdata.len+symsize+lcsize); + print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); } } @@ -812,6 +838,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- case 5: /* bra s */ v = -8; + // TODO: Use addrel. if(p->cond != P) v = (p->cond->pc - pc) - 8; o1 = opbra(p->as, p->scond); @@ -875,15 +902,22 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- rel = addrel(cursym); rel->off = pc - cursym->value; rel->siz = 4; - rel->type = D_ADDR; rel->sym = p->to.sym; rel->add = p->to.offset; + if(flag_shared) { + rel->type = D_PCREL; + rel->add += pc - p->pcrel->pc - 8; + } else + rel->type = D_ADDR; o1 = 0; } break; case 12: /* movw $lcon, reg */ o1 = omvl(p, &p->from, p->to.reg); + if(o->flag & LPCREL) { + o2 = oprrr(AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; + } break; case 13: /* op $lcon, [R], R */ @@ -1128,7 +1162,7 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- r = p->reg; if(r == NREG) { r = rt; - if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD) + if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD) r = 0; } o1 |= rf | (r<<16) | (rt<<12); @@ -1188,13 +1222,23 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- break; case 62: /* case R -> movw R<<2(PC),PC */ - o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); - o1 |= 2<<7; + if(o->flag & LPCREL) { + o1 = oprrr(AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; + o2 = olrr(REGTMP, REGPC, REGTMP, p->scond); + o2 |= 2<<7; + o3 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; + } else { + o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); + o1 |= 2<<7; + } break; case 63: /* bcase */ - if(p->cond != P) + if(p->cond != P) { o1 = p->cond->pc; + if(flag_shared) + o1 = o1 - p->pcrel->pc - 16; + } break; /* reloc ops */ @@ -1203,6 +1247,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 65: /* mov/movbu addr,R */ @@ -1212,6 +1260,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 = olr(0, REGTMP, p->to.reg, p->scond); if(p->as == AMOVBU || p->as == AMOVB) o2 |= 1<<22; + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 68: /* floating point store -> ADDR */ @@ -1219,6 +1271,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 69: /* floating point load <- ADDR */ @@ -1226,6 +1282,10 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- if(!o1) break; o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; /* ArmV4 ops: */ @@ -1422,12 +1482,57 @@ if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p- o2 ^= (1<<5)|(1<<6); else if(p->as == AMOVH) o2 ^= (1<<6); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } break; case 94: /* movh/movhu R,addr -> strh */ o1 = omvl(p, &p->to, REGTMP); if(!o1) break; o2 = oshr(p->from.reg, 0, REGTMP, p->scond); + if(o->flag & LPCREL) { + o3 = o2; + o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; + } + break; + case 95: /* PLD off(reg) */ + o1 = 0xf5d0f000; + o1 |= p->from.reg << 16; + if(p->from.offset < 0) { + o1 &= ~(1 << 23); + o1 |= (-p->from.offset) & 0xfff; + } else + o1 |= p->from.offset & 0xfff; + break; + case 96: /* UNDEF */ + // This is supposed to be something that stops execution. + // It's not supposed to be reached, ever, but if it is, we'd + // like to be able to tell how we got there. Assemble as + // BL $0 + // TODO: Use addrel. + v = (0 - pc) - 8; + o1 = opbra(ABL, C_SCOND_NONE); + o1 |= (v >> 2) & 0xffffff; + break; + case 97: /* CLZ Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg; + break; + case 98: /* MULW{T,B} Rs, Rm, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 16; + o1 |= p->from.reg << 8; + o1 |= p->reg; + break; + case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ + o1 = oprrr(p->as, p->scond); + o1 |= p->to.reg << 12; + o1 |= p->from.reg << 8; + o1 |= p->reg; + o1 |= p->to.offset << 16; break; } @@ -1547,6 +1652,8 @@ oprrr(int a, int sc) case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); + case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4); + case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4); case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); @@ -1586,6 +1693,19 @@ oprrr(int a, int sc) return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); case ACMP+AEND: // cmp imm return o | (0x3<<24) | (0x5<<20); + + case ACLZ: + // CLZ doesn't support .S + return (o & (0xf<<28)) | (0x16f<<16) | (0xf1<<4); + + case AMULWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xe<<4); + case AMULWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0xa<<4); + case AMULAWT: + return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); + case AMULAWB: + return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); } diag("bad rrr %d", a); prasm(curp); @@ -1789,7 +1909,8 @@ omvl(Prog *p, Adr *a, int dr) int chipzero(Ieee *e) { - if(e->l != 0 || e->h != 0) + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7 || e->l != 0 || e->h != 0) return -1; return 0; } @@ -1800,6 +1921,10 @@ chipfloat(Ieee *e) int n; ulong h; + // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. + if(goarm < 7) + goto no; + if(e->l != 0 || (e->h&0xffff) != 0) goto no; h = e->h & 0x7fc00000; @@ -1824,83 +1949,3 @@ chipfloat(Ieee *e) no: return -1; } - - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->hash) { - if(s->hide) - continue; - switch(s->type) { - case SCONST: - case SRODATA: - case SDATA: - case SELFROSECT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SNOPTRDATA: - case SSYMTAB: - case SPCLNTAB: - if(!s->reachable) - continue; - put(s, s->name, 'D', s->value, s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', s->value, s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(s = textp; s != nil; s = s->next) { - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); - Bflush(&bso); -} - -void -setpersrc(Sym *s) -{ - USED(s); -} diff --git a/src/cmd/5l/doc.go b/src/cmd/5l/doc.go index 969f502a7..a054a228b 100644 --- a/src/cmd/5l/doc.go +++ b/src/cmd/5l/doc.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. +// +build ignore + /* 5l is the linker for the ARM. @@ -10,4 +12,4 @@ The $GOARCH for these tools is arm. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index b1a48ded8..62dd8947f 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -36,7 +36,9 @@ enum { thechar = '5', - PtrSize = 4 + PtrSize = 4, + IntSize = 4, + FuncAlign = 4 // single-instruction alignment }; #ifndef EXTERN @@ -66,22 +68,24 @@ struct Adr { union { - int32 u0offset; + struct { + int32 u0offset; + int32 u0offset2; // argsize + } u0off; char* u0sval; Ieee u0ieee; char* u0sbig; } u0; Sym* sym; + Sym* gotype; char type; - uchar index; // not used on arm, required by ld/go.c char reg; char name; - int32 offset2; // argsize char class; - Sym* gotype; }; -#define offset u0.u0offset +#define offset u0.u0off.u0offset +#define offset2 u0.u0off.u0offset2 #define sval u0.u0sval #define scon sval #define ieee u0.u0ieee @@ -107,7 +111,7 @@ struct Prog } u0; Prog* cond; Prog* link; - Prog* dlink; + Prog* pcrel; int32 pc; int32 line; int32 spadj; @@ -116,7 +120,7 @@ struct Prog uchar as; uchar scond; uchar reg; - uchar align; + uchar align; // unused }; #define regused u0.u0regused @@ -135,8 +139,6 @@ struct Sym uchar reachable; uchar dynexport; uchar leaf; - uchar stkcheck; - uchar hide; int32 dynid; int32 plt; int32 got; @@ -144,18 +146,26 @@ struct Sym int32 sig; int32 size; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area uchar special; uchar fnptr; // used as fn ptr + uchar stkcheck; + uchar hide; Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub Sym* gotype; + Sym* reachparent; + Sym* queue; char* file; char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -168,6 +178,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; #define SIGNINTERN (1729*325*1729) @@ -190,6 +201,7 @@ struct Optab char size; char param; char flag; + uchar pcrelsiz; }; struct Oprang { @@ -207,10 +219,12 @@ enum LFROM = 1<<0, LTO = 1<<1, LPOOL = 1<<2, + LPCREL = 1<<3, C_NONE = 0, C_REG, C_REGREG, + C_REGREG2, C_SHIFT, C_FREG, C_PSR, @@ -220,6 +234,7 @@ enum C_NCON, /* ~RCON */ C_SCON, /* 0xffff */ C_LCON, + C_LCONADDR, C_ZFCON, C_SFCON, C_LFCON, @@ -273,6 +288,7 @@ EXTERN int32 INITDAT; /* data location */ EXTERN int32 INITRND; /* data round above text location */ EXTERN int32 INITTEXT; /* text location */ EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN int32 autosize; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -280,7 +296,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN Sym* etextp; EXTERN char* noname; EXTERN Prog* lastp; @@ -298,12 +314,15 @@ EXTERN char* rpath; EXTERN uint32 stroffset; EXTERN int32 symsize; EXTERN Sym* textp; -EXTERN int32 textsize; EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; +EXTERN int tlsoffset; EXTERN int armsize; +EXTERN int goarm; +EXTERN Sym* adrgotype; // type symbol on last Adr read +EXTERN Sym* fromgotype; // type symbol on last p->from read extern char* anames[]; extern Optab optab[]; @@ -312,6 +331,8 @@ void addpool(Prog*, Adr*); EXTERN Prog* blitrl; EXTERN Prog* elitrl; +EXTERN int goarm; + void initdiv(void); EXTERN Prog* prog_div; EXTERN Prog* prog_divu; @@ -398,6 +419,9 @@ void span(void); void strnput(char*, int); int32 symaddr(Sym*); void undef(void); +void vputb(uint64); +void vputl(uint64); +void wputb(uint16); void wput(int32); void wputl(ushort w); void xdefine(char*, int, int32); @@ -407,8 +431,9 @@ int32 immaddr(int32); int32 opbra(int, int); int brextra(Prog*); int isbranch(Prog*); -void fnptrs(void); +void fnptrs(void); void doelf(void); +void dozerostk(void); // used by -Z vlong addaddr(Sym *s, Sym *t); vlong addsize(Sym *s, Sym *t); @@ -425,3 +450,9 @@ vlong adduintxx(Sym *s, uint64 v, int wid); #define VPUT(a) abort() #endif + +/* Used by ../ld/dwarf.c */ +enum +{ + DWARFREGSP = 13 +}; diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 293fee3c6..a051774b4 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -225,6 +225,12 @@ Dconv(Fmt *fp) snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); break; + case D_REGREG2: + snprint(str, sizeof str, "R%d,R%d", a->reg, (int)a->offset); + if(a->name != D_NONE || a->sym != S) + snprint(str, sizeof str, "%N(R%d)(REG)", a, a->reg); + break; + case D_FREG: snprint(str, sizeof str, "F%d", a->reg); if(a->name != D_NONE || a->sym != S) @@ -429,6 +435,7 @@ cnames[] = [C_LAUTO] = "C_LAUTO", [C_LBRA] = "C_LBRA", [C_LCON] = "C_LCON", + [C_LCONADDR] = "C_LCONADDR", [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", @@ -438,6 +445,7 @@ cnames[] = [C_RCON] = "C_RCON", [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", + [C_REGREG2] = "C_REGREG2", [C_ROREG] = "C_ROREG", [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index 004f9f2fa..99a096a31 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -45,6 +45,20 @@ static Sym* sym_divu; static Sym* sym_mod; static Sym* sym_modu; +static void +linkcase(Prog *casep) +{ + Prog *p; + + for(p = casep; p != P; p = p->link){ + if(p->as == ABCASE) { + for(; p != P && p->as == ABCASE; p = p->link) + p->pcrel = casep; + break; + } + } +} + void noops(void) { @@ -76,6 +90,11 @@ noops(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { switch(p->as) { + case ACASE: + if(flag_shared) + linkcase(p); + break; + case ATEXT: p->mark |= LEAF; break; @@ -365,11 +384,7 @@ noops(void) q1 = p; /* MOV a,4(SP) */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -379,11 +394,7 @@ noops(void) p->to.offset = 4; /* MOV b,REGTMP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -395,11 +406,7 @@ noops(void) p->to.offset = 0; /* CALL appropriate */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = ABL; p->line = q1->line; p->to.type = D_BRANCH; @@ -424,11 +431,7 @@ noops(void) } /* MOV REGTMP, b */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; @@ -438,12 +441,9 @@ noops(void) p->to.reg = q1->to.reg; /* ADD $8,SP */ - q = prg(); - q->link = p->link; - p->link = q; - p = q; - + p = appendp(p); p->as = AADD; + p->line = q1->line; p->from.type = D_CONST; p->from.reg = NREG; p->from.offset = 8; diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index a3f816160..6aa7fdd69 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -34,6 +34,7 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/dwarf.h" #include <ar.h> #ifndef DEFAULT @@ -47,41 +48,29 @@ Header headers[] = { "noheader", Hnoheader, "risc", Hrisc, "plan9", Hplan9x32, - "netbsd", Hnetbsd, "ixp1200", Hixp1200, "ipaq", Hipaq, "linux", Hlinux, + "freebsd", Hfreebsd, + "netbsd", Hnetbsd, 0, 0 }; /* * -Hrisc -T0x10005000 -R4 is aif for risc os * -Hplan9 -T4128 -R4096 is plan9 format - * -Hnetbsd -T0xF0000020 -R4 is NetBSD format * -Hixp1200 is IXP1200 (raw) * -Hipaq -T0xC0008010 -R1024 is ipaq * -Hlinux -Tx -Rx is linux elf + * -Hfreebsd is freebsd elf + * -Hnetbsd is netbsd elf */ -static char* -linkername[] = -{ - "runtime.softfloat", - "math.sqrtGoC", -}; - -void -usage(void) -{ - fprint(2, "usage: 5l [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-D data] [-R rnd] [-r path] [-o out] main.5\n"); - errorexit(); -} - void main(int argc, char *argv[]) { - int c, i; - char *p, *name, *val; + char *p; + Sym *s; Binit(&bso, 1, OWRITE); listinit(); @@ -92,59 +81,53 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - p = getenv("GOARM"); - if(p != nil && strcmp(p, "5") == 0) + p = getgoarm(); + if(p != nil) + goarm = atoi(p); + else + goarm = 6; + if(goarm == 5) debug['F'] = 1; - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - /* do something about setting INITTEXT */ - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND - - USED(argc); + flagcount("1", "use alternate profiling code", &debug['1']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagint32("D", "addr: data address", &INITDAT); + flagcount("G", "debug pseudo-ops", &debug['G']); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("M", "disable software div/mod", &debug['M']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("P", "debug code generation", &debug['P']); + flagint32("R", "rnd: address rounding", &INITRND); + flagint32("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagcount("n", "dump symbol table", &debug['n']); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + flagcount("shared", "generate shared object", &flag_shared); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -152,7 +135,7 @@ main(int argc, char *argv[]) libinit(); if(HEADTYPE == -1) - HEADTYPE = Hlinux; + HEADTYPE = headtype(goos); switch(HEADTYPE) { default: diag("unknown -H option"); @@ -184,15 +167,6 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = 4096; break; - case Hnetbsd: /* boot for NetBSD */ - HEADR = 32L; - if(INITTEXT == -1) - INITTEXT = 0xF0000020L; - if(INITDAT == -1) - INITDAT = 0; - if(INITRND == -1) - INITRND = 4096; - break; case Hixp1200: /* boot for IXP1200 */ HEADR = 0L; if(INITTEXT == -1) @@ -212,7 +186,11 @@ main(int argc, char *argv[]) INITRND = 1024; break; case Hlinux: /* arm elf */ - debug['d'] = 1; // no dynamic linking + case Hfreebsd: + case Hnetbsd: + debug['d'] = 0; // with dynamic linking + tlsoffset = -8; // hardcoded number, first 4-byte word for g, and then 4-byte word for m + // this number is known to ../../pkg/runtime/cgo/gcc_linux_arm.c elfinit(); HEADR = ELFRESERVE; if(INITTEXT == -1) @@ -246,13 +224,17 @@ main(int argc, char *argv[]) cbp = buf.cbuf; cbc = sizeof(buf.cbuf); + // embed goarm to runtime.goarm + s = lookup("runtime.goarm", 0); + s->dupok = 1; + adduint8(s, goarm); + addlibpath("command line", "command line", argv[0], "main"); loadlib(); // mark some functions that are only referenced after linker code editing - // TODO(kaib): this doesn't work, the prog can't be found in runtime - for(i=0; i<nelem(linkername); i++) - mark(lookup(linkername[i], 0)); + if(debug['F']) + mark(rlookup("_sfloat", 0)); deadcode(); if(textp == nil) { diag("no code"); @@ -268,9 +250,16 @@ main(int argc, char *argv[]) doelf(); follow(); softfloat(); - noops(); + // 5l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + if(debug['Z']) + dozerostk(); + noops(); // generate stack split prolog, handle div/mod, etc. dostkcheck(); span(); + addexport(); + // textaddress() functionality is handled in span() pclntab(); symtab(); dodata(); @@ -291,24 +280,38 @@ main(int argc, char *argv[]) errorexit(); } +static Sym* +zsym(char *pn, Biobuf *f, Sym *h[]) +{ + int o; + + o = BGETC(f); + if(o == 0) + return S; + if(o < 0 || o >= NSYM || h[o] == nil) + mangle(pn); + return h[o]; +} + static void -zaddr(Biobuf *f, Adr *a, Sym *h[]) +zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) { int i, c; int32 l; Sym *s; Auto *u; - a->type = Bgetc(f); - a->reg = Bgetc(f); - c = Bgetc(f); + a->type = BGETC(f); + a->reg = BGETC(f); + c = BGETC(f); if(c < 0 || c > NSYM){ print("sym out of range: %d\n", c); Bputc(f, ALAST+1); return; } a->sym = h[c]; - a->name = Bgetc(f); + a->name = BGETC(f); + adrgotype = zsym(pn, f, h); if((schar)a->reg < 0 || a->reg > NREG) { print("register out of range %d\n", a->reg); @@ -341,7 +344,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) break; case D_REGREG: - a->offset = Bgetc(f); + case D_REGREG2: + a->offset = BGETC(f); break; case D_CONST2: @@ -368,8 +372,11 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) if(s == S) return; i = a->name; - if(i != D_AUTO && i != D_PARAM) + if(i != D_AUTO && i != D_PARAM) { + if(s && adrgotype) + s->gotype = adrgotype; return; + } l = a->offset; for(u=curauto; u; u=u->link) @@ -377,6 +384,8 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) if(u->type == i) { if(u->aoffset > l) u->aoffset = l; + if(adrgotype) + u->gotype = adrgotype; return; } @@ -386,6 +395,7 @@ zaddr(Biobuf *f, Adr *a, Sym *h[]) u->asym = s; u->aoffset = l; u->type = i; + u->gotype = adrgotype; } void @@ -425,7 +435,7 @@ newloop: loop: if(f->state == Bracteof || Boffset(f) >= eof) goto eof; - o = Bgetc(f); + o = BGETC(f); if(o == Beof) goto eof; @@ -438,8 +448,8 @@ loop: sig = 0; if(o == ASIGNAME) sig = Bget4(f); - v = Bgetc(f); /* type */ - o = Bgetc(f); /* sym */ + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ r = 0; if(v == D_STATIC) r = version; @@ -483,18 +493,20 @@ loop: histfrogp++; } else collapsefrog(s); + dwarfaddfrag(s->value, s->name); } goto loop; } p = mal(sizeof(Prog)); p->as = o; - p->scond = Bgetc(f); - p->reg = Bgetc(f); + p->scond = BGETC(f); + p->reg = BGETC(f); p->line = Bget4(f); - zaddr(f, &p->from, h); - zaddr(f, &p->to, h); + zaddr(pn, f, &p->from, h); + fromgotype = adrgotype; + zaddr(pn, f, &p->to, h); if(p->as != ATEXT && p->as != AGLOBL && p->reg > NREG) diag("register out of range %A %d", p->as, p->reg); @@ -582,6 +594,15 @@ loop: pc++; break; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + break; + + case ATYPE: + pc++; + goto loop; + case ATEXT: if(cursym != nil && cursym->text) { histtoauto(); @@ -611,6 +632,11 @@ loop: etextp->next = s; else textp = s; + if(fromgotype) { + if(s->gotype && s->gotype != fromgotype) + diag("%s: type mismatch for %s", pn, s->name); + s->gotype = fromgotype; + } etextp = s; p->align = 4; autosize = (p->to.offset+3L) & ~3L; @@ -619,6 +645,7 @@ loop: s->type = STEXT; s->text = p; s->value = pc; + s->args = p->to.offset2; lastp = p; p->pc = pc; pc++; @@ -672,7 +699,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -694,7 +721,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SBSS; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 514786f85..231071f20 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -63,6 +63,7 @@ Optab optab[] = { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, { ABL, C_NONE, C_NONE, C_ROREG, 7, 8, 0 }, + { ABL, C_REG, C_NONE, C_ROREG, 7, 8, 0 }, { ABX, C_NONE, C_NONE, C_ROREG, 75, 12, 0 }, { ABXRET, C_NONE, C_NONE, C_ROREG, 76, 4, 0 }, @@ -77,10 +78,12 @@ Optab optab[] = { ASWI, C_NONE, C_NONE, C_LCON, 10, 4, 0 }, { AWORD, C_NONE, C_NONE, C_LCON, 11, 4, 0 }, + { AWORD, C_NONE, C_NONE, C_LCONADDR, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, { AMOVW, C_LCON, C_NONE, C_REG, 12, 4, 0, LFROM }, + { AMOVW, C_LCONADDR, C_NONE, C_REG, 12, 4, 0, LFROM | LPCREL, 4}, { AADD, C_NCON, C_REG, C_REG, 13, 8, 0 }, { AADD, C_NCON, C_NONE, C_REG, 13, 8, 0 }, @@ -103,6 +106,7 @@ Optab optab[] = { ADIV, C_REG, C_NONE, C_REG, 16, 4, 0 }, { AMULL, C_REG, C_REG, C_REGREG, 17, 4, 0 }, + { AMULA, C_REG, C_REG, C_REGREG2, 17, 4, 0 }, { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 4, REGSP }, { AMOVW, C_REG, C_NONE, C_SOREG, 20, 4, 0 }, @@ -118,20 +122,20 @@ Optab optab[] = { AMOVW, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVW, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVW, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVB, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVB, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVB, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVBU, C_REG, C_NONE, C_LAUTO, 30, 8, REGSP, LTO }, { AMOVBU, C_REG, C_NONE, C_LOREG, 30, 8, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO }, + { AMOVBU, C_REG, C_NONE, C_ADDR, 64, 8, 0, LTO | LPCREL, 4 }, { AMOVW, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVW, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, - { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, + { AMOVW, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, { AMOVBU, C_LAUTO,C_NONE, C_REG, 31, 8, REGSP, LFROM }, { AMOVBU, C_LOREG,C_NONE, C_REG, 31, 8, 0, LFROM }, - { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM }, + { AMOVBU, C_ADDR, C_NONE, C_REG, 65, 8, 0, LFROM | LPCREL, 4 }, { AMOVW, C_LACON,C_NONE, C_REG, 34, 8, REGSP, LFROM }, @@ -158,8 +162,8 @@ Optab optab[] = { AMOVF, C_LAUTO,C_NONE, C_FREG, 53, 12, REGSP, LFROM }, { AMOVF, C_LOREG,C_NONE, C_FREG, 53, 12, 0, LFROM }, - { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO }, - { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM }, + { AMOVF, C_FREG, C_NONE, C_ADDR, 68, 8, 0, LTO | LPCREL, 4 }, + { AMOVF, C_ADDR, C_NONE, C_FREG, 69, 8, 0, LFROM | LPCREL, 4}, { AADDF, C_FREG, C_NONE, C_FREG, 54, 4, 0 }, { AADDF, C_FREG, C_REG, C_FREG, 54, 4, 0 }, @@ -177,7 +181,7 @@ Optab optab[] = { AMOVB, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, { AMOVBU, C_REG, C_NONE, C_SHIFT, 61, 4, 0 }, - { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, + { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0, LPCREL, 8 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, @@ -194,20 +198,20 @@ Optab optab[] = { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO }, { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO }, + { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO | LPCREL, 4 }, { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM }, { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM }, + { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM | LPCREL, 4 }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, @@ -232,5 +236,16 @@ Optab optab[] = { ALDREXD, C_SOREG,C_NONE, C_REG, 91, 4, 0 }, { ASTREXD, C_SOREG,C_REG, C_REG, 92, 4, 0 }, + { APLD, C_SOREG,C_NONE, C_NONE, 95, 4, 0 }, + + { AUNDEF, C_NONE, C_NONE, C_NONE, 96, 4, 0 }, + + { ACLZ, C_REG, C_NONE, C_REG, 97, 4, 0 }, + + { AMULWT, C_REG, C_REG, C_REG, 98, 4, 0 }, + { AMULAWT, C_REG, C_REG, C_REGREG2, 99, 4, 0 }, + + { AUSEFIELD, C_ADDR, C_NONE, C_NONE, 0, 0, 0 }, + { AXXX, C_NONE, C_NONE, C_NONE, 0, 4, 0 }, }; diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 0f2afbd85..c22b86085 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -119,7 +119,7 @@ loop: i--; continue; } - if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) + if(a == AB || (a == ARET && q->scond == 14) || a == ARFE || a == AUNDEF) goto copy; if(q->cond == P || (q->cond->mark&FOLL)) continue; @@ -140,7 +140,7 @@ loop: } (*last)->link = r; *last = r; - if(a == AB || (a == ARET && q->scond == 14) || a == ARFE) + if(a == AB || (a == ARET && q->scond == 14) || a == ARFE || a == AUNDEF) return; r->as = ABNE; if(a == ABNE) @@ -166,7 +166,7 @@ loop: p->mark |= FOLL; (*last)->link = p; *last = p; - if(a == AB || (a == ARET && p->scond == 14) || a == ARFE){ + if(a == AB || (a == ARET && p->scond == 14) || a == ARFE || a == AUNDEF){ return; } if(p->cond != P) @@ -215,7 +215,7 @@ patch(void) s = p->to.sym; if(s->text == nil) continue; - switch(s->type) { + switch(s->type&SMASK) { default: diag("undefined: %s", s->name); s->type = STEXT; @@ -231,7 +231,7 @@ patch(void) if(p->to.type != D_BRANCH) continue; c = p->to.offset; - for(q = textp->text; q != P;) { + for(q = cursym->text; q != P;) { if(c == q->pc) break; if(q->forwd != P && c >= q->forwd->pc) @@ -333,3 +333,70 @@ rnd(int32 v, int32 r) v -= c; return v; } + +void +dozerostk(void) +{ + Prog *p, *pl; + int32 autoffset; + + for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(cursym->text == nil || cursym->text->link == nil) + continue; + p = cursym->text; + autoffset = p->to.offset; + if(autoffset < 0) + autoffset = 0; + if(autoffset && !(p->reg&NOSPLIT)) { + // MOVW $4(R13), R1 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = 13; + p->from.offset = 4; + p->to.type = D_REG; + p->to.reg = 1; + + // MOVW $n(R13), R2 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.reg = 13; + p->from.offset = 4 + autoffset; + p->to.type = D_REG; + p->to.reg = 2; + + // MOVW $0, R3 + p = appendp(p); + p->as = AMOVW; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_REG; + p->to.reg = 3; + + // L: + // MOVW.P R3, 0(R1) +4 + // CMP R1, R2 + // BNE L + p = pl = appendp(p); + p->as = AMOVW; + p->from.type = D_REG; + p->from.reg = 3; + p->to.type = D_OREG; + p->to.reg = 1; + p->to.offset = 4; + p->scond |= C_PBIT; + + p = appendp(p); + p->as = ACMP; + p->from.type = D_REG; + p->from.reg = 1; + p->reg = 2; + + p = appendp(p); + p->as = ABNE; + p->to.type = D_BRANCH; + p->cond = pl; + } + } +} diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 401107178..de6481c71 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -55,6 +55,8 @@ softfloat(void) case ADIVD: case ASQRTF: case ASQRTD: + case AABSF: + case AABSD: goto soft; default: @@ -74,6 +76,7 @@ softfloat(void) p->to.type = D_BRANCH; p->to.sym = symsfloat; p->cond = psfloat; + p->line = next->line; p = next; wasfloat = 1; diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index 13e1848e1..a5afa02e7 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -90,16 +90,36 @@ span(void) int32 c, otxt, out[6]; Section *sect; uchar *bp; + Sym *sub; if(debug['v']) Bprint(&bso, "%5.2f span\n", cputime()); Bflush(&bso); + sect = addsection(&segtext, ".text", 05); + lookup("text", 0)->sect = sect; + lookup("etext", 0)->sect = sect; + bflag = 0; c = INITTEXT; otxt = c; for(cursym = textp; cursym != nil; cursym = cursym->next) { + cursym->sect = sect; p = cursym->text; + if(p == P || p->link == P) { // handle external functions and ELF section symbols + if(cursym->type & SSUB) + continue; + if(cursym->align != 0) + c = rnd(c, cursym->align); + cursym->value = 0; + for(sub = cursym; sub != S; sub = sub->sub) { + sub->value += c; + for(p = sub->text; p != P; p = p->link) + p->pc += sub->value; + } + c += cursym->size; + continue; + } p->pc = c; cursym->value = c; @@ -160,6 +180,8 @@ span(void) bflag = 0; c = INITTEXT; for(cursym = textp; cursym != nil; cursym = cursym->next) { + if(!cursym->text || !cursym->text->link) + continue; cursym->value = c; for(p = cursym->text; p != P; p = p->link) { curp = p; @@ -217,6 +239,8 @@ span(void) */ for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; + if(p == P || p->link == P) + continue; autosize = p->to.offset + 4; symgrow(cursym, cursym->size); @@ -235,7 +259,6 @@ span(void) } } } - sect = addsection(&segtext, ".text", 05); sect->vaddr = INITTEXT; sect->len = c - INITTEXT; } @@ -269,12 +292,20 @@ flushpool(Prog *p, int skip, int force) q->to.type = D_BRANCH; q->cond = p->link; q->link = blitrl; + q->line = p->line; blitrl = q; } else if(!force && (p->pc+pool.size-pool.start < 2048)) return 0; elitrl->link = p->link; p->link = blitrl; + // BUG(minux): how to correctly handle line number for constant pool entries? + // for now, we set line number to the last instruction preceding them at least + // this won't bloat the .debug_line tables + while(blitrl) { + blitrl->line = p->line; + blitrl = blitrl->link; + } blitrl = 0; /* BUG: should refer back to values until out-of-range */ elitrl = 0; pool.size = 0; @@ -299,9 +330,11 @@ addpool(Prog *p, Adr *a) switch(c) { default: t.to = *a; + if(flag_shared && t.to.sym != S) + t.pcrel = p; break; - case C_SROREG: + case C_SROREG: case C_LOREG: case C_ROREG: case C_FOREG: @@ -316,11 +349,13 @@ addpool(Prog *p, Adr *a) break; } - for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */ - if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { - p->cond = q; - return; - } + if(t.pcrel == P) { + for(q = blitrl; q != P; q = q->link) /* could hash on t.t0.offset */ + if(q->pcrel == P && memcmp(&q->to, &t.to, sizeof(t.to)) == 0) { + p->cond = q; + return; + } + } q = prg(); *q = t; @@ -407,25 +442,9 @@ immhalf(int32 v) int32 symaddr(Sym *s) { - int32 v; - - v = s->value; - switch(s->type) { - default: - diag("unexpected type %d in symaddr(%s)", s->type, s->name); - return 0; - - case STEXT: - case SELFROSECT: - case SRODATA: - case SDATA: - case SBSS: - case SCONST: - case SNOPTRDATA: - case SNOPTRBSS: - break; - } - return v; + if(!s->reachable) + diag("unreachable symbol in symaddr - %s", s->name); + return s->value; } int @@ -444,6 +463,9 @@ aclass(Adr *a) case D_REGREG: return C_REGREG; + case D_REGREG2: + return C_REGREG2; + case D_SHIFT: return C_SHIFT; @@ -552,7 +574,10 @@ aclass(Adr *a) if(s == S) break; instoffset = 0; // s.b. unused but just in case - return C_LCON; + if(flag_shared) + return C_LCONADDR; + else + return C_LCON; case D_AUTO: instoffset = autosize + a->offset; @@ -718,8 +743,14 @@ buildop(void) for(i=0; i<C_GOK; i++) for(n=0; n<C_GOK; n++) xcmp[i][n] = cmp(n, i); - for(n=0; optab[n].as != AXXX; n++) - ; + for(n=0; optab[n].as != AXXX; n++) { + if((optab[n].flag & LPCREL) != 0) { + if(flag_shared) + optab[n].size += optab[n].pcrelsiz; + else + optab[n].flag &= ~LPCREL; + } + } qsort(optab, n, sizeof(optab[0]), ocmp); for(i=0; i<n; i++) { r = optab[i].as; @@ -798,8 +829,11 @@ buildop(void) case AMOVM: case ARFE: case ATEXT: + case AUSEFIELD: + case ALOCALS: case ACASE: case ABCASE: + case ATYPE: break; case AADDF: oprange[AADDD] = oprange[r]; @@ -813,6 +847,8 @@ buildop(void) oprange[ASQRTD] = oprange[r]; oprange[AMOVFD] = oprange[r]; oprange[AMOVDF] = oprange[r]; + oprange[AABSF] = oprange[r]; + oprange[AABSD] = oprange[r]; break; case ACMPF: @@ -832,17 +868,28 @@ buildop(void) break; case AMULL: - oprange[AMULA] = oprange[r]; oprange[AMULAL] = oprange[r]; oprange[AMULLU] = oprange[r]; oprange[AMULALU] = oprange[r]; break; + case AMULWT: + oprange[AMULWB] = oprange[r]; + break; + + case AMULAWT: + oprange[AMULAWB] = oprange[r]; + break; + + case AMULA: case ALDREX: case ASTREX: case ALDREXD: case ASTREXD: case ATST: + case APLD: + case AUNDEF: + case ACLZ: break; } } diff --git a/src/cmd/6a/a.y b/src/cmd/6a/a.y index 8459ff323..42af65e35 100644 --- a/src/cmd/6a/a.y +++ b/src/cmd/6a/a.y @@ -176,6 +176,11 @@ nonrel: $$.from = nullgen; $$.to = $1; } +| imm ',' rel + { + $$.from = $1; + $$.to = $3; + } spec1: /* DATA */ nam '/' con ',' imm diff --git a/src/cmd/6a/doc.go b/src/cmd/6a/doc.go index 92fb74de6..a5f3f87f0 100644 --- a/src/cmd/6a/doc.go +++ b/src/cmd/6a/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 6a is a version of the Plan 9 assembler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2a + http://plan9.bell-labs.com/magic/man2html/1/8a Its target architecture is the x86-64, referred to by these tools as amd64. */ -package documentation +package main diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index e013bec2a..d65802a20 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -340,6 +340,8 @@ struct "BSRL", LTYPE3, ABSRL, "BSRQ", LTYPE3, ABSRQ, "BSRW", LTYPE3, ABSRW, + "BSWAPL", LTYPE1, ABSWAPL, + "BSWAPQ", LTYPE1, ABSWAPQ, "BTCL", LTYPE3, ABTCL, "BTCQ", LTYPE3, ABTCQ, "BTCW", LTYPE3, ABTCW, @@ -1001,6 +1003,19 @@ struct "XORPS", LTYPE3, AXORPS, "CRC32B", LTYPE4, ACRC32B, "CRC32Q", LTYPE4, ACRC32Q, + "PREFETCHT0", LTYPE2, APREFETCHT0, + "PREFETCHT1", LTYPE2, APREFETCHT1, + "PREFETCHT2", LTYPE2, APREFETCHT2, + "PREFETCHNTA", LTYPE2, APREFETCHNTA, + "UNDEF", LTYPE0, AUNDEF, + "AESENC", LTYPE3, AAESENC, + "AESENCLAST", LTYPE3, AAESENCLAST, + "AESDEC", LTYPE3, AAESDEC, + "AESDECLAST", LTYPE3, AAESDECLAST, + "AESIMC", LTYPE3, AAESIMC, + "AESKEYGENASSIST", LTYPEX, AAESKEYGENASSIST, + "PSHUFD", LTYPEX, APSHUFD, + "USEFIELD", LTYPEN, AUSEFIELD, 0 }; @@ -1250,11 +1265,38 @@ outhist(void) Hist *h; char *p, *q, *op, c; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; g = nullgen; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -1306,21 +1348,12 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); - } -} -void -pragbldicks(void) -{ - while(getnsc() != '\n') - ; -} - -void -praghjdicks(void) -{ - while(getnsc() != '\n') - ; + if(tofree) { + free(tofree); + tofree = nil; + } + } } #include "../cc/lexbody" diff --git a/src/cmd/6a/y.tab.c b/src/cmd/6a/y.tab.c index 2da8a1fac..75c4ad5ea 100644 --- a/src/cmd/6a/y.tab.c +++ b/src/cmd/6a/y.tab.c @@ -406,16 +406,16 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 548 +#define YYLAST 554 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 54 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 40 /* YYNRULES -- Number of rules. */ -#define YYNRULES 131 +#define YYNRULES 132 /* YYNRULES -- Number of states. */ -#define YYNSTATES 260 +#define YYNSTATES 263 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -468,16 +468,16 @@ static const yytype_uint16 yyprhs[] = 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, 85, 88, 89, 91, 95, 99, 102, 104, 107, 109, - 112, 114, 120, 124, 130, 133, 135, 137, 139, 143, - 149, 153, 159, 162, 164, 168, 174, 180, 181, 183, - 187, 193, 195, 197, 199, 201, 204, 207, 209, 211, - 213, 215, 220, 223, 226, 228, 230, 232, 234, 236, - 238, 240, 243, 246, 249, 252, 255, 260, 266, 270, - 272, 274, 276, 281, 286, 291, 298, 308, 312, 316, - 322, 331, 333, 340, 346, 354, 355, 358, 361, 363, - 365, 367, 369, 371, 374, 377, 380, 384, 386, 389, - 393, 398, 400, 404, 408, 412, 416, 420, 425, 430, - 434, 438 + 112, 114, 118, 124, 128, 134, 137, 139, 141, 143, + 147, 153, 157, 163, 166, 168, 172, 178, 184, 185, + 187, 191, 197, 199, 201, 203, 205, 208, 211, 213, + 215, 217, 219, 224, 227, 230, 232, 234, 236, 238, + 240, 242, 244, 247, 250, 253, 256, 259, 264, 270, + 274, 276, 278, 280, 285, 290, 295, 302, 312, 316, + 320, 326, 335, 337, 344, 350, 358, 359, 362, 365, + 367, 369, 371, 373, 375, 378, 381, 384, 388, 390, + 393, 397, 402, 404, 408, 412, 416, 420, 424, 429, + 434, 438, 442 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ @@ -494,40 +494,40 @@ static const yytype_int8 yyrhs[] = 75, -1, 29, 76, -1, 23, 77, -1, -1, 49, -1, 80, 49, 78, -1, 78, 49, 80, -1, 80, 49, -1, 80, -1, 49, 78, -1, 78, -1, 49, - 81, -1, 81, -1, 88, 11, 91, 49, 84, -1, - 85, 49, 83, -1, 85, 49, 91, 49, 83, -1, - 49, 79, -1, 79, -1, 61, -1, 65, -1, 80, - 49, 78, -1, 80, 49, 78, 46, 35, -1, 80, - 49, 78, -1, 80, 49, 78, 46, 36, -1, 80, - 49, -1, 80, -1, 80, 49, 78, -1, 82, 49, - 78, 49, 91, -1, 84, 49, 78, 49, 82, -1, - -1, 84, -1, 85, 49, 84, -1, 85, 49, 91, - 49, 84, -1, 82, -1, 85, -1, 81, -1, 87, - -1, 10, 82, -1, 10, 86, -1, 82, -1, 86, - -1, 78, -1, 84, -1, 91, 50, 32, 51, -1, - 43, 89, -1, 44, 89, -1, 34, -1, 37, -1, - 35, -1, 38, -1, 42, -1, 36, -1, 39, -1, - 52, 92, -1, 52, 91, -1, 52, 88, -1, 52, - 41, -1, 52, 40, -1, 52, 50, 40, 51, -1, - 52, 50, 9, 40, 51, -1, 52, 9, 40, -1, - 86, -1, 87, -1, 91, -1, 91, 50, 35, 51, - -1, 91, 50, 42, 51, -1, 91, 50, 36, 51, - -1, 91, 50, 35, 10, 91, 51, -1, 91, 50, - 35, 51, 50, 35, 10, 91, 51, -1, 50, 35, - 51, -1, 50, 42, 51, -1, 50, 35, 10, 91, - 51, -1, 50, 35, 51, 50, 35, 10, 91, 51, - -1, 88, -1, 88, 50, 35, 10, 91, 51, -1, - 43, 89, 50, 90, 51, -1, 43, 6, 7, 89, - 50, 33, 51, -1, -1, 8, 91, -1, 9, 91, - -1, 33, -1, 42, -1, 31, -1, 30, -1, 45, - -1, 9, 91, -1, 8, 91, -1, 53, 91, -1, - 50, 93, 51, -1, 30, -1, 9, 30, -1, 30, - 9, 30, -1, 9, 30, 9, 30, -1, 91, -1, - 93, 8, 93, -1, 93, 9, 93, -1, 93, 10, - 93, -1, 93, 11, 93, -1, 93, 12, 93, -1, - 93, 6, 6, 93, -1, 93, 7, 7, 93, -1, - 93, 5, 93, -1, 93, 4, 93, -1, 93, 3, - 93, -1 + 81, -1, 81, -1, 84, 49, 81, -1, 88, 11, + 91, 49, 84, -1, 85, 49, 83, -1, 85, 49, + 91, 49, 83, -1, 49, 79, -1, 79, -1, 61, + -1, 65, -1, 80, 49, 78, -1, 80, 49, 78, + 46, 35, -1, 80, 49, 78, -1, 80, 49, 78, + 46, 36, -1, 80, 49, -1, 80, -1, 80, 49, + 78, -1, 82, 49, 78, 49, 91, -1, 84, 49, + 78, 49, 82, -1, -1, 84, -1, 85, 49, 84, + -1, 85, 49, 91, 49, 84, -1, 82, -1, 85, + -1, 81, -1, 87, -1, 10, 82, -1, 10, 86, + -1, 82, -1, 86, -1, 78, -1, 84, -1, 91, + 50, 32, 51, -1, 43, 89, -1, 44, 89, -1, + 34, -1, 37, -1, 35, -1, 38, -1, 42, -1, + 36, -1, 39, -1, 52, 92, -1, 52, 91, -1, + 52, 88, -1, 52, 41, -1, 52, 40, -1, 52, + 50, 40, 51, -1, 52, 50, 9, 40, 51, -1, + 52, 9, 40, -1, 86, -1, 87, -1, 91, -1, + 91, 50, 35, 51, -1, 91, 50, 42, 51, -1, + 91, 50, 36, 51, -1, 91, 50, 35, 10, 91, + 51, -1, 91, 50, 35, 51, 50, 35, 10, 91, + 51, -1, 50, 35, 51, -1, 50, 42, 51, -1, + 50, 35, 10, 91, 51, -1, 50, 35, 51, 50, + 35, 10, 91, 51, -1, 88, -1, 88, 50, 35, + 10, 91, 51, -1, 43, 89, 50, 90, 51, -1, + 43, 6, 7, 89, 50, 33, 51, -1, -1, 8, + 91, -1, 9, 91, -1, 33, -1, 42, -1, 31, + -1, 30, -1, 45, -1, 9, 91, -1, 8, 91, + -1, 53, 91, -1, 50, 93, 51, -1, 30, -1, + 9, 30, -1, 30, 9, 30, -1, 9, 30, 9, + 30, -1, 91, -1, 93, 8, 93, -1, 93, 9, + 93, -1, 93, 10, 93, -1, 93, 11, 93, -1, + 93, 12, 93, -1, 93, 6, 6, 93, -1, 93, + 7, 7, 93, -1, 93, 5, 93, -1, 93, 4, + 93, -1, 93, 3, 93, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -537,16 +537,16 @@ static const yytype_uint16 yyrline[] = 86, 87, 90, 95, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 120, 124, 131, 138, 145, 150, 157, 162, 169, - 174, 181, 189, 194, 202, 207, 214, 215, 218, 223, - 233, 238, 248, 253, 258, 265, 273, 283, 287, 294, - 299, 307, 308, 311, 312, 313, 317, 321, 322, 325, - 326, 329, 335, 344, 353, 358, 363, 368, 373, 378, - 383, 389, 397, 403, 414, 420, 426, 432, 438, 446, - 447, 450, 456, 462, 468, 474, 483, 492, 497, 502, - 510, 520, 524, 533, 540, 549, 552, 556, 562, 563, - 567, 570, 571, 575, 579, 583, 587, 593, 597, 601, - 606, 613, 614, 618, 622, 626, 630, 634, 638, 642, - 646, 650 + 174, 179, 186, 194, 199, 207, 212, 219, 220, 223, + 228, 238, 243, 253, 258, 263, 270, 278, 288, 292, + 299, 304, 312, 313, 316, 317, 318, 322, 326, 327, + 330, 331, 334, 340, 349, 358, 363, 368, 373, 378, + 383, 388, 394, 402, 408, 419, 425, 431, 437, 443, + 451, 452, 455, 461, 467, 473, 479, 488, 497, 502, + 507, 515, 525, 529, 538, 545, 554, 557, 561, 567, + 568, 572, 575, 576, 580, 584, 588, 592, 598, 602, + 606, 611, 618, 619, 623, 627, 631, 635, 639, 643, + 647, 651, 655 }; #endif @@ -591,16 +591,16 @@ static const yytype_uint8 yyr1[] = 57, 57, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, 63, 64, 64, 65, 65, 66, - 66, 67, 68, 68, 69, 69, 70, 70, 71, 71, - 72, 72, 73, 73, 73, 74, 75, 76, 76, 77, - 77, 78, 78, 79, 79, 79, 79, 79, 79, 80, - 80, 81, 81, 81, 82, 82, 82, 82, 82, 82, - 82, 83, 84, 84, 84, 84, 84, 84, 84, 85, - 85, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 87, 87, 88, 88, 89, 89, 89, 90, 90, - 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, - 92, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93 + 66, 66, 67, 68, 68, 69, 69, 70, 70, 71, + 71, 72, 72, 73, 73, 73, 74, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 79, 79, 79, 79, + 80, 80, 81, 81, 81, 82, 82, 82, 82, 82, + 82, 82, 83, 84, 84, 84, 84, 84, 84, 84, + 85, 85, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 88, 88, 89, 89, 89, 90, + 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, + 92, 92, 93, 93, 93, 93, 93, 93, 93, 93, + 93, 93, 93 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -610,16 +610,16 @@ static const yytype_uint8 yyr2[] = 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 3, 3, 2, 1, 2, 1, 2, - 1, 5, 3, 5, 2, 1, 1, 1, 3, 5, - 3, 5, 2, 1, 3, 5, 5, 0, 1, 3, - 5, 1, 1, 1, 1, 2, 2, 1, 1, 1, - 1, 4, 2, 2, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 4, 5, 3, 1, - 1, 1, 4, 4, 4, 6, 9, 3, 3, 5, - 8, 1, 6, 5, 7, 0, 2, 2, 1, 1, - 1, 1, 1, 2, 2, 2, 3, 1, 2, 3, - 4, 1, 3, 3, 3, 3, 3, 4, 4, 3, - 3, 3 + 1, 3, 5, 3, 5, 2, 1, 1, 1, 3, + 5, 3, 5, 2, 1, 3, 5, 5, 0, 1, + 3, 5, 1, 1, 1, 1, 2, 2, 1, 1, + 1, 1, 4, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 4, 5, 3, + 1, 1, 1, 4, 4, 4, 6, 9, 3, 3, + 5, 8, 1, 6, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 1, 2, + 3, 4, 1, 3, 3, 3, 3, 3, 4, 4, + 3, 3, 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -629,39 +629,40 @@ static const yytype_uint8 yydefact[] = { 2, 3, 1, 0, 0, 31, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 57, 0, 0, 0, 9, 4, 0, 11, 32, - 14, 0, 0, 111, 74, 76, 79, 75, 77, 80, - 78, 105, 112, 0, 0, 0, 15, 38, 61, 62, - 89, 90, 101, 91, 0, 16, 69, 36, 70, 17, - 0, 18, 0, 0, 105, 105, 0, 22, 45, 63, - 67, 68, 64, 91, 20, 0, 32, 46, 47, 23, - 105, 0, 0, 19, 40, 0, 21, 0, 30, 0, - 24, 0, 25, 0, 26, 53, 27, 0, 28, 0, - 29, 58, 7, 0, 5, 0, 10, 114, 113, 0, - 0, 0, 0, 37, 0, 0, 121, 0, 115, 0, - 0, 0, 85, 84, 0, 83, 82, 35, 0, 0, - 65, 66, 72, 73, 44, 0, 0, 72, 39, 0, - 0, 0, 0, 0, 52, 0, 0, 0, 12, 0, - 13, 105, 106, 107, 0, 0, 97, 98, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 116, 0, - 0, 0, 0, 88, 0, 0, 33, 34, 0, 0, - 0, 42, 0, 59, 0, 48, 50, 54, 0, 0, - 8, 6, 0, 110, 108, 109, 0, 0, 0, 131, - 130, 129, 0, 0, 122, 123, 124, 125, 126, 0, - 0, 92, 94, 93, 0, 86, 71, 0, 0, 117, - 81, 0, 0, 0, 0, 0, 0, 0, 103, 99, - 0, 127, 128, 0, 0, 0, 87, 41, 118, 0, - 43, 60, 49, 51, 55, 56, 0, 0, 102, 95, - 0, 0, 119, 104, 0, 0, 120, 100, 0, 96 + 0, 58, 0, 0, 0, 9, 4, 0, 11, 32, + 14, 0, 0, 112, 75, 77, 80, 76, 78, 81, + 79, 106, 113, 0, 0, 0, 15, 38, 62, 63, + 90, 91, 102, 92, 0, 16, 70, 36, 71, 17, + 0, 18, 0, 0, 106, 106, 0, 22, 46, 64, + 68, 69, 65, 92, 20, 0, 32, 47, 48, 23, + 106, 0, 0, 19, 40, 0, 0, 21, 0, 30, + 0, 24, 0, 25, 0, 26, 54, 27, 0, 28, + 0, 29, 59, 7, 0, 5, 0, 10, 115, 114, + 0, 0, 0, 0, 37, 0, 0, 122, 0, 116, + 0, 0, 0, 86, 85, 0, 84, 83, 35, 0, + 0, 66, 67, 73, 74, 45, 0, 0, 73, 39, + 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, + 12, 0, 13, 106, 107, 108, 0, 0, 98, 99, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 117, 0, 0, 0, 0, 89, 0, 0, 33, 34, + 0, 0, 41, 0, 43, 0, 60, 0, 49, 51, + 55, 0, 0, 8, 6, 0, 111, 109, 110, 0, + 0, 0, 132, 131, 130, 0, 0, 123, 124, 125, + 126, 127, 0, 0, 93, 95, 94, 0, 87, 72, + 0, 0, 118, 82, 0, 0, 0, 0, 0, 0, + 0, 104, 100, 0, 128, 129, 0, 0, 0, 88, + 42, 119, 0, 44, 61, 50, 52, 56, 57, 0, + 0, 103, 96, 0, 0, 120, 105, 0, 0, 121, + 101, 0, 97 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 26, 149, 147, 27, 30, 59, 61, - 55, 46, 83, 74, 86, 67, 79, 90, 92, 94, - 96, 98, 100, 88, 56, 68, 57, 69, 48, 181, - 58, 49, 50, 51, 52, 112, 196, 53, 220, 117 + -1, 1, 3, 26, 151, 149, 27, 30, 59, 61, + 55, 46, 83, 74, 87, 67, 79, 91, 93, 95, + 97, 99, 101, 89, 56, 68, 57, 69, 48, 184, + 58, 49, 50, 51, 52, 113, 199, 53, 223, 118 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing @@ -669,41 +670,42 @@ static const yytype_int16 yydefgoto[] = #define YYPACT_NINF -97 static const yytype_int16 yypact[] = { - -97, 29, -97, 206, -39, -36, 254, 278, 278, 326, - 230, 15, 302, 59, 412, 412, 278, 278, 278, 82, - -20, -20, -22, -6, 2, -97, -97, 12, -97, -97, - -97, 476, 476, -97, -97, -97, -97, -97, -97, -97, - -97, 88, -97, 326, 366, 476, -97, -97, -97, -97, - -97, -97, 14, 16, 364, -97, -97, 33, -97, -97, - 39, -97, 41, 350, 88, 11, 152, -97, -97, -97, - -97, -97, -97, 31, -97, 51, 326, -97, -97, -97, - 11, 382, 476, -97, -97, 42, -97, 50, -97, 56, - -97, 58, -97, 61, -97, 64, -97, 66, -97, 79, - -97, -97, -97, 476, -97, 476, -97, -97, -97, 84, - 476, 476, 92, -97, 18, 78, -97, 129, -97, 108, - -5, 414, -97, -97, 421, -97, -97, -97, 326, 278, - -97, -97, 92, -97, -97, 13, 476, -97, -97, 112, - 428, 438, 326, 326, 326, 326, 326, 206, 527, 206, - 527, 11, -97, -97, 10, 476, 96, -97, 476, 476, - 476, 141, 144, 476, 476, 476, 476, 476, -97, 142, - 23, 126, 127, -97, 467, 128, -97, -97, 130, 110, - 8, -97, 114, -97, 134, 138, 139, -97, 149, 154, - -97, -97, 163, -97, -97, -97, 153, 165, 179, 536, - 493, 159, 476, 476, 65, 65, -97, -97, -97, 476, - 476, 168, -97, -97, 185, -97, -97, -20, 211, 233, - -97, 191, -20, 209, 210, 476, 82, 214, -97, -97, - 238, 200, 200, 201, 203, 220, -97, -97, 247, 227, - -97, -97, -97, -97, -97, -97, 207, 476, -97, -97, - 249, 231, -97, -97, 219, 476, -97, -97, 225, -97 + -97, 40, -97, 208, 5, -4, 140, 295, 295, 343, + 233, 15, 319, 381, 91, 91, 295, 295, 295, 222, + 24, 24, -15, 22, 14, -97, -97, 30, -97, -97, + -97, 486, 486, -97, -97, -97, -97, -97, -97, -97, + -97, 42, -97, 343, 406, 486, -97, -97, -97, -97, + -97, -97, 45, 48, 399, -97, -97, 20, -97, -97, + 67, -97, 68, 367, 42, 19, 271, -97, -97, -97, + -97, -97, -97, 87, -97, 127, 343, -97, -97, -97, + 19, 437, 486, -97, -97, 90, 92, -97, 94, -97, + 96, -97, 104, -97, 105, -97, 111, -97, 118, -97, + 119, -97, -97, -97, 486, -97, 486, -97, -97, -97, + 133, 486, 486, 121, -97, 8, 122, -97, 80, -97, + 134, 78, 413, -97, -97, 446, -97, -97, -97, 343, + 295, -97, -97, 121, -97, -97, 7, 486, -97, -97, + 437, 148, 453, 462, 343, 343, 343, 343, 343, 208, + 284, 208, 284, 19, -97, -97, -1, 486, 131, -97, + 486, 486, 486, 166, 177, 486, 486, 486, 486, 486, + -97, 176, 10, 136, 137, -97, 480, 141, -97, -97, + 143, 142, -97, 16, -97, 149, -97, 150, 151, 154, + -97, 152, 155, -97, -97, 156, -97, -97, -97, 157, + 159, 170, 99, 535, 542, 486, 486, 26, 26, -97, + -97, -97, 486, 486, 161, -97, -97, 162, -97, -97, + 24, 184, 198, -97, 163, 24, 181, 183, 486, 222, + 205, -97, -97, 229, 114, 114, 193, 194, 211, -97, + -97, 238, 219, -97, -97, -97, -97, -97, -97, 199, + 486, -97, -97, 244, 232, -97, -97, 214, 486, -97, + -97, 215, -97 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -97, -97, -97, -96, -97, -97, -97, 259, -97, -97, - -97, 265, -97, -97, -97, -97, -97, -97, -97, -97, - -97, -97, -97, -97, 30, 212, -2, -11, -9, 60, - -16, 32, -3, -1, 7, -53, -97, -10, -97, -80 + -97, -97, -97, -96, -97, -97, -97, 261, -97, -97, + -97, 262, -97, -97, -97, -97, -97, -97, -97, -97, + -97, -97, -97, -97, 17, 218, -2, -11, -9, 61, + -8, 51, 1, -3, -7, -56, -97, -10, -97, -87 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If @@ -713,120 +715,122 @@ static const yytype_int16 yypgoto[] = #define YYTABLE_NINF -1 static const yytype_uint16 yytable[] = { - 73, 70, 84, 85, 99, 101, 60, 71, 28, 72, - 97, 132, 133, 29, 91, 93, 95, 218, 75, 110, - 111, 107, 108, 148, 102, 150, 103, 137, 155, 2, - 170, 171, 54, 210, 116, 118, 47, 172, 219, 62, - 104, 193, 47, 194, 126, 178, 87, 89, 170, 171, - 105, 190, 195, 191, 130, 172, 73, 70, 41, 106, - 131, 125, 136, 71, 119, 72, 120, 31, 32, 156, - 138, 85, 116, 113, 211, 165, 166, 167, 199, 200, - 201, 135, 127, 204, 205, 206, 207, 208, 128, 33, - 129, 151, 139, 116, 109, 116, 110, 111, 192, 140, - 152, 153, 80, 65, 42, 141, 113, 142, 81, 82, - 143, 108, 45, 144, 116, 145, 34, 35, 36, 37, - 38, 39, 231, 232, 40, 183, 179, 177, 146, 157, - 182, 184, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 154, 169, 178, 197, 198, 202, 116, 116, - 116, 203, 209, 116, 116, 116, 116, 116, 176, 217, - 31, 32, 63, 221, 108, 161, 162, 163, 164, 165, - 166, 167, 185, 186, 187, 188, 189, 212, 213, 215, - 168, 216, 33, 222, 223, 224, 34, 35, 36, 37, - 38, 39, 116, 116, 40, 64, 65, 42, 225, 233, - 234, 237, 44, 226, 228, 45, 241, 4, 163, 164, - 165, 166, 167, 227, 230, 244, 229, 245, 235, 5, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 236, 254, 31, 32, - 63, 238, 239, 180, 242, 258, 243, 246, 247, 22, - 23, 24, 248, 25, 249, 250, 251, 252, 253, 255, - 33, 256, 31, 32, 34, 35, 36, 37, 38, 39, - 257, 77, 40, 64, 65, 42, 259, 78, 134, 66, - 44, 240, 0, 45, 33, 0, 31, 32, 34, 35, - 36, 37, 38, 39, 0, 0, 40, 41, 0, 42, - 0, 0, 0, 43, 44, 0, 0, 45, 33, 0, - 31, 32, 34, 35, 36, 37, 38, 39, 0, 0, - 40, 41, 0, 42, 0, 0, 0, 0, 44, 0, - 54, 45, 33, 0, 31, 32, 34, 35, 36, 37, - 38, 39, 0, 0, 40, 41, 0, 42, 0, 0, - 0, 76, 44, 0, 0, 45, 33, 0, 31, 32, - 34, 35, 36, 37, 38, 39, 0, 0, 40, 41, - 0, 42, 31, 121, 31, 32, 44, 0, 0, 45, - 33, 0, 0, 0, 34, 35, 36, 37, 38, 39, - 31, 32, 40, 0, 33, 42, 33, 0, 0, 0, - 44, 114, 0, 45, 122, 123, 0, 41, 115, 42, - 0, 42, 33, 0, 124, 0, 82, 45, 0, 45, - 31, 32, 31, 32, 0, 80, 65, 42, 0, 31, - 174, 0, 82, 0, 0, 45, 31, 32, 0, 0, - 0, 0, 33, 0, 33, 0, 31, 32, 0, 0, - 0, 33, 0, 0, 173, 41, 0, 42, 33, 42, - 0, 175, 44, 0, 82, 45, 42, 45, 33, 0, - 0, 82, 0, 42, 45, 31, 32, 0, 82, 0, - 180, 45, 0, 42, 31, 32, 0, 0, 82, 0, - 54, 45, 0, 0, 0, 0, 0, 33, 160, 161, - 162, 163, 164, 165, 166, 167, 33, 214, 0, 0, - 0, 0, 42, 0, 0, 0, 0, 82, 0, 0, - 45, 42, 0, 0, 0, 0, 82, 0, 0, 45, - 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, - 159, 160, 161, 162, 163, 164, 165, 166, 167 + 73, 70, 84, 86, 75, 85, 60, 72, 133, 134, + 98, 71, 100, 102, 92, 94, 96, 150, 157, 152, + 213, 108, 109, 47, 138, 221, 62, 111, 112, 47, + 196, 103, 197, 104, 117, 119, 167, 168, 169, 180, + 2, 198, 172, 173, 127, 29, 222, 126, 110, 174, + 111, 112, 28, 193, 131, 194, 73, 70, 41, 158, + 114, 214, 106, 72, 132, 88, 90, 71, 105, 128, + 139, 86, 117, 202, 203, 204, 54, 107, 207, 208, + 209, 210, 211, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 114, 117, 120, 117, 195, 121, 31, + 32, 154, 155, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 109, 172, 173, 117, 129, 130, 234, 235, + 174, 33, 165, 166, 167, 168, 169, 181, 179, 182, + 86, 170, 185, 187, 41, 186, 42, 136, 137, 140, + 153, 44, 141, 142, 45, 143, 178, 200, 31, 32, + 117, 117, 117, 144, 145, 117, 117, 117, 117, 117, + 146, 188, 189, 190, 191, 192, 109, 147, 148, 171, + 33, 156, 205, 159, 34, 35, 36, 37, 38, 39, + 180, 201, 40, 41, 206, 42, 212, 215, 216, 43, + 44, 220, 218, 45, 219, 117, 117, 226, 224, 225, + 227, 228, 236, 237, 229, 233, 230, 242, 231, 4, + 232, 238, 240, 239, 241, 183, 245, 244, 247, 246, + 248, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 249, 250, + 257, 31, 32, 63, 251, 252, 253, 254, 261, 255, + 256, 22, 23, 24, 258, 25, 34, 35, 36, 37, + 38, 39, 259, 33, 40, 260, 262, 34, 35, 36, + 37, 38, 39, 77, 78, 40, 64, 65, 42, 31, + 32, 63, 66, 44, 135, 243, 45, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 0, 0, 0, + 0, 33, 0, 31, 32, 34, 35, 36, 37, 38, + 39, 0, 0, 40, 64, 65, 42, 0, 0, 0, + 0, 44, 0, 0, 45, 33, 0, 31, 32, 34, + 35, 36, 37, 38, 39, 0, 0, 40, 41, 0, + 42, 0, 0, 0, 0, 44, 0, 54, 45, 33, + 0, 31, 32, 34, 35, 36, 37, 38, 39, 0, + 0, 40, 41, 0, 42, 0, 0, 0, 76, 44, + 0, 0, 45, 33, 0, 31, 32, 34, 35, 36, + 37, 38, 39, 0, 0, 40, 41, 0, 42, 31, + 32, 0, 0, 44, 0, 0, 45, 33, 0, 0, + 0, 34, 35, 36, 37, 38, 39, 31, 122, 40, + 0, 33, 42, 0, 31, 32, 0, 44, 0, 0, + 45, 31, 32, 0, 80, 65, 42, 0, 0, 33, + 81, 82, 0, 54, 45, 0, 33, 0, 0, 123, + 124, 115, 41, 33, 42, 31, 32, 0, 116, 125, + 0, 42, 45, 175, 31, 176, 82, 0, 42, 45, + 0, 31, 32, 82, 0, 0, 45, 33, 0, 0, + 31, 32, 0, 0, 0, 0, 33, 0, 0, 0, + 80, 65, 42, 33, 0, 0, 177, 82, 31, 32, + 45, 42, 33, 0, 31, 32, 82, 0, 42, 45, + 0, 0, 0, 82, 0, 183, 45, 42, 0, 0, + 33, 0, 82, 0, 54, 45, 33, 0, 0, 0, + 217, 0, 0, 0, 0, 42, 0, 0, 0, 0, + 82, 42, 0, 45, 0, 0, 82, 0, 0, 45, + 162, 163, 164, 165, 166, 167, 168, 169, 163, 164, + 165, 166, 167, 168, 169 }; static const yytype_int16 yycheck[] = { - 10, 10, 13, 13, 20, 21, 8, 10, 47, 10, - 19, 64, 65, 49, 16, 17, 18, 9, 11, 8, - 9, 31, 32, 103, 46, 105, 48, 80, 10, 0, - 35, 36, 52, 10, 44, 45, 6, 42, 30, 9, - 46, 31, 12, 33, 54, 32, 14, 15, 35, 36, - 48, 147, 42, 149, 63, 42, 66, 66, 43, 47, - 63, 54, 11, 66, 50, 66, 50, 8, 9, 51, - 81, 81, 82, 43, 51, 10, 11, 12, 158, 159, - 160, 50, 49, 163, 164, 165, 166, 167, 49, 30, - 49, 7, 50, 103, 6, 105, 8, 9, 151, 49, - 110, 111, 43, 44, 45, 49, 76, 49, 49, 50, - 49, 121, 53, 49, 124, 49, 34, 35, 36, 37, - 38, 39, 202, 203, 42, 141, 136, 129, 49, 51, - 140, 141, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 50, 35, 32, 155, 50, 6, 158, 159, - 160, 7, 10, 163, 164, 165, 166, 167, 128, 49, - 8, 9, 10, 49, 174, 6, 7, 8, 9, 10, - 11, 12, 142, 143, 144, 145, 146, 51, 51, 51, - 51, 51, 30, 49, 46, 46, 34, 35, 36, 37, - 38, 39, 202, 203, 42, 43, 44, 45, 49, 209, - 210, 217, 50, 49, 51, 53, 222, 1, 8, 9, - 10, 11, 12, 50, 35, 225, 51, 226, 50, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 51, 247, 8, 9, - 10, 30, 9, 52, 35, 255, 36, 33, 10, 43, - 44, 45, 51, 47, 51, 35, 9, 30, 51, 10, - 30, 30, 8, 9, 34, 35, 36, 37, 38, 39, - 51, 12, 42, 43, 44, 45, 51, 12, 66, 49, - 50, 221, -1, 53, 30, -1, 8, 9, 34, 35, - 36, 37, 38, 39, -1, -1, 42, 43, -1, 45, - -1, -1, -1, 49, 50, -1, -1, 53, 30, -1, - 8, 9, 34, 35, 36, 37, 38, 39, -1, -1, - 42, 43, -1, 45, -1, -1, -1, -1, 50, -1, - 52, 53, 30, -1, 8, 9, 34, 35, 36, 37, - 38, 39, -1, -1, 42, 43, -1, 45, -1, -1, - -1, 49, 50, -1, -1, 53, 30, -1, 8, 9, - 34, 35, 36, 37, 38, 39, -1, -1, 42, 43, - -1, 45, 8, 9, 8, 9, 50, -1, -1, 53, - 30, -1, -1, -1, 34, 35, 36, 37, 38, 39, - 8, 9, 42, -1, 30, 45, 30, -1, -1, -1, - 50, 35, -1, 53, 40, 41, -1, 43, 42, 45, - -1, 45, 30, -1, 50, -1, 50, 53, -1, 53, - 8, 9, 8, 9, -1, 43, 44, 45, -1, 8, - 9, -1, 50, -1, -1, 53, 8, 9, -1, -1, - -1, -1, 30, -1, 30, -1, 8, 9, -1, -1, - -1, 30, -1, -1, 40, 43, -1, 45, 30, 45, - -1, 40, 50, -1, 50, 53, 45, 53, 30, -1, - -1, 50, -1, 45, 53, 8, 9, -1, 50, -1, - 52, 53, -1, 45, 8, 9, -1, -1, 50, -1, - 52, 53, -1, -1, -1, -1, -1, 30, 5, 6, - 7, 8, 9, 10, 11, 12, 30, 40, -1, -1, - -1, -1, 45, -1, -1, -1, -1, 50, -1, -1, - 53, 45, -1, -1, -1, -1, 50, -1, -1, 53, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 4, 5, 6, 7, 8, 9, 10, 11, 12 + 10, 10, 13, 13, 11, 13, 8, 10, 64, 65, + 19, 10, 20, 21, 16, 17, 18, 104, 10, 106, + 10, 31, 32, 6, 80, 9, 9, 8, 9, 12, + 31, 46, 33, 48, 44, 45, 10, 11, 12, 32, + 0, 42, 35, 36, 54, 49, 30, 54, 6, 42, + 8, 9, 47, 149, 63, 151, 66, 66, 43, 51, + 43, 51, 48, 66, 63, 14, 15, 66, 46, 49, + 81, 81, 82, 160, 161, 162, 52, 47, 165, 166, + 167, 168, 169, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 76, 104, 50, 106, 153, 50, 8, + 9, 111, 112, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 122, 35, 36, 125, 49, 49, 205, 206, + 42, 30, 8, 9, 10, 11, 12, 137, 130, 140, + 140, 51, 142, 143, 43, 143, 45, 50, 11, 49, + 7, 50, 50, 49, 53, 49, 129, 157, 8, 9, + 160, 161, 162, 49, 49, 165, 166, 167, 168, 169, + 49, 144, 145, 146, 147, 148, 176, 49, 49, 35, + 30, 50, 6, 51, 34, 35, 36, 37, 38, 39, + 32, 50, 42, 43, 7, 45, 10, 51, 51, 49, + 50, 49, 51, 53, 51, 205, 206, 46, 49, 49, + 46, 49, 212, 213, 49, 35, 50, 9, 51, 1, + 51, 50, 220, 51, 30, 52, 35, 225, 228, 36, + 229, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 33, 10, + 250, 8, 9, 10, 51, 51, 35, 9, 258, 30, + 51, 43, 44, 45, 10, 47, 34, 35, 36, 37, + 38, 39, 30, 30, 42, 51, 51, 34, 35, 36, + 37, 38, 39, 12, 12, 42, 43, 44, 45, 8, + 9, 10, 49, 50, 66, 224, 53, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, + -1, 30, -1, 8, 9, 34, 35, 36, 37, 38, + 39, -1, -1, 42, 43, 44, 45, -1, -1, -1, + -1, 50, -1, -1, 53, 30, -1, 8, 9, 34, + 35, 36, 37, 38, 39, -1, -1, 42, 43, -1, + 45, -1, -1, -1, -1, 50, -1, 52, 53, 30, + -1, 8, 9, 34, 35, 36, 37, 38, 39, -1, + -1, 42, 43, -1, 45, -1, -1, -1, 49, 50, + -1, -1, 53, 30, -1, 8, 9, 34, 35, 36, + 37, 38, 39, -1, -1, 42, 43, -1, 45, 8, + 9, -1, -1, 50, -1, -1, 53, 30, -1, -1, + -1, 34, 35, 36, 37, 38, 39, 8, 9, 42, + -1, 30, 45, -1, 8, 9, -1, 50, -1, -1, + 53, 8, 9, -1, 43, 44, 45, -1, -1, 30, + 49, 50, -1, 52, 53, -1, 30, -1, -1, 40, + 41, 35, 43, 30, 45, 8, 9, -1, 42, 50, + -1, 45, 53, 40, 8, 9, 50, -1, 45, 53, + -1, 8, 9, 50, -1, -1, 53, 30, -1, -1, + 8, 9, -1, -1, -1, -1, 30, -1, -1, -1, + 43, 44, 45, 30, -1, -1, 40, 50, 8, 9, + 53, 45, 30, -1, 8, 9, 50, -1, 45, 53, + -1, -1, -1, 50, -1, 52, 53, 45, -1, -1, + 30, -1, 50, -1, 52, 53, 30, -1, -1, -1, + 40, -1, -1, -1, -1, 45, -1, -1, -1, -1, + 50, 45, -1, 53, -1, -1, 50, -1, -1, 53, + 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, + 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -841,24 +845,25 @@ static const yytype_uint8 yystos[] = 86, 87, 88, 91, 52, 64, 78, 80, 84, 62, 80, 63, 78, 10, 43, 44, 49, 69, 79, 81, 82, 86, 87, 91, 67, 88, 49, 61, 65, 70, - 43, 49, 50, 66, 81, 91, 68, 85, 77, 85, - 71, 80, 72, 80, 73, 80, 74, 82, 75, 84, - 76, 84, 46, 48, 46, 48, 47, 91, 91, 6, - 8, 9, 89, 78, 35, 42, 91, 93, 91, 50, - 50, 9, 40, 41, 50, 88, 91, 49, 49, 49, - 82, 86, 89, 89, 79, 50, 11, 89, 81, 50, - 49, 49, 49, 49, 49, 49, 49, 59, 93, 58, - 93, 7, 91, 91, 50, 10, 51, 51, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 51, 35, - 35, 36, 42, 40, 9, 40, 78, 80, 32, 91, - 52, 83, 91, 84, 91, 78, 78, 78, 78, 78, - 57, 57, 89, 31, 33, 42, 90, 91, 50, 93, - 93, 93, 6, 7, 93, 93, 93, 93, 93, 10, - 10, 51, 51, 51, 40, 51, 51, 49, 9, 30, - 92, 49, 49, 46, 46, 49, 49, 50, 51, 51, - 35, 93, 93, 91, 91, 50, 51, 84, 30, 9, - 83, 84, 35, 36, 91, 82, 33, 10, 51, 51, - 35, 9, 30, 51, 91, 10, 30, 51, 91, 51 + 43, 49, 50, 66, 81, 84, 91, 68, 85, 77, + 85, 71, 80, 72, 80, 73, 80, 74, 82, 75, + 84, 76, 84, 46, 48, 46, 48, 47, 91, 91, + 6, 8, 9, 89, 78, 35, 42, 91, 93, 91, + 50, 50, 9, 40, 41, 50, 88, 91, 49, 49, + 49, 82, 86, 89, 89, 79, 50, 11, 89, 81, + 49, 50, 49, 49, 49, 49, 49, 49, 49, 59, + 93, 58, 93, 7, 91, 91, 50, 10, 51, 51, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 51, 35, 35, 36, 42, 40, 9, 40, 78, 80, + 32, 91, 81, 52, 83, 91, 84, 91, 78, 78, + 78, 78, 78, 57, 57, 89, 31, 33, 42, 90, + 91, 50, 93, 93, 93, 6, 7, 93, 93, 93, + 93, 93, 10, 10, 51, 51, 51, 40, 51, 51, + 49, 9, 30, 92, 49, 49, 46, 46, 49, 49, + 50, 51, 51, 35, 93, 93, 91, 91, 50, 51, + 84, 30, 9, 83, 84, 35, 36, 91, 82, 33, + 10, 51, 51, 35, 9, 30, 51, 91, 10, 30, + 51, 91, 51 }; #define yyerrok (yyerrstatus = 0) @@ -1879,7 +1884,15 @@ yyreduce: break; case 41: -#line 182 "a.y" +#line 180 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; + + case 42: +#line 187 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1887,16 +1900,16 @@ yyreduce: } break; - case 42: -#line 190 "a.y" + case 43: +#line 195 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 43: -#line 195 "a.y" + case 44: +#line 200 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1904,32 +1917,32 @@ yyreduce: } break; - case 44: -#line 203 "a.y" + case 45: +#line 208 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 45: -#line 208 "a.y" + case 46: +#line 213 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 48: -#line 219 "a.y" + case 49: +#line 224 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 49: -#line 224 "a.y" + case 50: +#line 229 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1939,16 +1952,16 @@ yyreduce: } break; - case 50: -#line 234 "a.y" + case 51: +#line 239 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 51: -#line 239 "a.y" + case 52: +#line 244 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1958,32 +1971,32 @@ yyreduce: } break; - case 52: -#line 249 "a.y" + case 53: +#line 254 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 53: -#line 254 "a.y" + case 54: +#line 259 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 54: -#line 259 "a.y" + case 55: +#line 264 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 55: -#line 266 "a.y" + case 56: +#line 271 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1991,8 +2004,8 @@ yyreduce: } break; - case 56: -#line 274 "a.y" + case 57: +#line 279 "a.y" { (yyval.gen2).from = (yyvsp[(3) - (5)].gen); (yyval.gen2).to = (yyvsp[(5) - (5)].gen); @@ -2002,32 +2015,32 @@ yyreduce: } break; - case 57: -#line 283 "a.y" + case 58: +#line 288 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 58: -#line 288 "a.y" + case 59: +#line 293 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 59: -#line 295 "a.y" + case 60: +#line 300 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 60: -#line 300 "a.y" + case 61: +#line 305 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2035,22 +2048,22 @@ yyreduce: } break; - case 65: -#line 314 "a.y" + case 66: +#line 319 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 66: -#line 318 "a.y" + case 67: +#line 323 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 71: -#line 330 "a.y" + case 72: +#line 335 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2058,8 +2071,8 @@ yyreduce: } break; - case 72: -#line 336 "a.y" + case 73: +#line 341 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2070,8 +2083,8 @@ yyreduce: } break; - case 73: -#line 345 "a.y" + case 74: +#line 350 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2080,14 +2093,6 @@ yyreduce: } break; - case 74: -#line 354 "a.y" - { - (yyval.gen) = nullgen; - (yyval.gen).type = (yyvsp[(1) - (1)].lval); - } - break; - case 75: #line 359 "a.y" { @@ -2116,7 +2121,7 @@ yyreduce: #line 374 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = D_SP; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; @@ -2124,7 +2129,7 @@ yyreduce: #line 379 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = (yyvsp[(1) - (1)].lval); + (yyval.gen).type = D_SP; } break; @@ -2137,16 +2142,15 @@ yyreduce: break; case 81: -#line 390 "a.y" +#line 389 "a.y" { (yyval.gen) = nullgen; - (yyval.gen).type = D_CONST; - (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; case 82: -#line 398 "a.y" +#line 395 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2155,7 +2159,16 @@ yyreduce: break; case 83: -#line 404 "a.y" +#line 403 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = D_CONST; + (yyval.gen).offset = (yyvsp[(2) - (2)].lval); + } + break; + + case 84: +#line 409 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2168,8 +2181,8 @@ yyreduce: } break; - case 84: -#line 415 "a.y" + case 85: +#line 420 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2177,8 +2190,8 @@ yyreduce: } break; - case 85: -#line 421 "a.y" + case 86: +#line 426 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2186,8 +2199,8 @@ yyreduce: } break; - case 86: -#line 427 "a.y" + case 87: +#line 432 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2195,8 +2208,8 @@ yyreduce: } break; - case 87: -#line 433 "a.y" + case 88: +#line 438 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2204,8 +2217,8 @@ yyreduce: } break; - case 88: -#line 439 "a.y" + case 89: +#line 444 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2213,8 +2226,8 @@ yyreduce: } break; - case 91: -#line 451 "a.y" + case 92: +#line 456 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2222,8 +2235,8 @@ yyreduce: } break; - case 92: -#line 457 "a.y" + case 93: +#line 462 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2231,8 +2244,8 @@ yyreduce: } break; - case 93: -#line 463 "a.y" + case 94: +#line 468 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2240,8 +2253,8 @@ yyreduce: } break; - case 94: -#line 469 "a.y" + case 95: +#line 474 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2249,8 +2262,8 @@ yyreduce: } break; - case 95: -#line 475 "a.y" + case 96: +#line 480 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2261,8 +2274,8 @@ yyreduce: } break; - case 96: -#line 484 "a.y" + case 97: +#line 489 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2273,24 +2286,24 @@ yyreduce: } break; - case 97: -#line 493 "a.y" + case 98: +#line 498 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 98: -#line 498 "a.y" + case 99: +#line 503 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 99: -#line 503 "a.y" + case 100: +#line 508 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2300,8 +2313,8 @@ yyreduce: } break; - case 100: -#line 511 "a.y" + case 101: +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2311,15 +2324,15 @@ yyreduce: } break; - case 101: -#line 521 "a.y" + case 102: +#line 526 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 102: -#line 525 "a.y" + case 103: +#line 530 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2328,8 +2341,8 @@ yyreduce: } break; - case 103: -#line 534 "a.y" + case 104: +#line 539 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2338,8 +2351,8 @@ yyreduce: } break; - case 104: -#line 541 "a.y" + case 105: +#line 546 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2348,164 +2361,164 @@ yyreduce: } break; - case 105: -#line 549 "a.y" + case 106: +#line 554 "a.y" { (yyval.lval) = 0; } break; - case 106: -#line 553 "a.y" + case 107: +#line 558 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 107: -#line 557 "a.y" + case 108: +#line 562 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 109: -#line 564 "a.y" + case 110: +#line 569 "a.y" { (yyval.lval) = D_AUTO; } break; - case 112: -#line 572 "a.y" + case 113: +#line 577 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 113: -#line 576 "a.y" + case 114: +#line 581 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 114: -#line 580 "a.y" + case 115: +#line 585 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 115: -#line 584 "a.y" + case 116: +#line 589 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 116: -#line 588 "a.y" + case 117: +#line 593 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 117: -#line 594 "a.y" + case 118: +#line 599 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].lval) & 0xffffffffLL; } break; - case 118: -#line 598 "a.y" + case 119: +#line 603 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval) & 0xffffffffLL; } break; - case 119: -#line 602 "a.y" + case 120: +#line 607 "a.y" { (yyval.lval) = ((yyvsp[(1) - (3)].lval) & 0xffffffffLL) + (((yyvsp[(3) - (3)].lval) & 0xffffLL) << 32); } break; - case 120: -#line 607 "a.y" + case 121: +#line 612 "a.y" { (yyval.lval) = (-(yyvsp[(2) - (4)].lval) & 0xffffffffLL) + (((yyvsp[(4) - (4)].lval) & 0xffffLL) << 32); } break; - case 122: -#line 615 "a.y" + case 123: +#line 620 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 123: -#line 619 "a.y" + case 124: +#line 624 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 124: -#line 623 "a.y" + case 125: +#line 628 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 125: -#line 627 "a.y" + case 126: +#line 632 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 126: -#line 631 "a.y" + case 127: +#line 636 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 127: -#line 635 "a.y" + case 128: +#line 640 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 128: -#line 639 "a.y" + case 129: +#line 644 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 129: -#line 643 "a.y" + case 130: +#line 648 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 130: -#line 647 "a.y" + case 131: +#line 652 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 131: -#line 651 "a.y" + case 132: +#line 656 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2513,7 +2526,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 2517 "y.tab.c" +#line 2530 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 71822586c..95400c445 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -265,6 +265,18 @@ cgen(Node *n, Node *nn) break; } } + if(n->op == OOR && l->op == OASHL && r->op == OLSHR + && l->right->op == OCONST && r->right->op == OCONST + && l->left->op == ONAME && r->left->op == ONAME + && l->left->sym == r->left->sym + && l->right->vconst + r->right->vconst == 8 * l->left->type->width) { + regalloc(&nod, l->left, nn); + cgen(l->left, &nod); + gopcode(OROTL, n->type, l->right, &nod); + gmove(&nod, nn); + regfree(&nod); + break; + } if(n->op == OADD && l->op == OASHL && l->right->op == OCONST && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) { c = l->right->vconst; @@ -1672,7 +1684,7 @@ copy: regsalloc(&nod2, nn); nn->type = t; - gins(AMOVL, &nod1, &nod2); + gins(AMOVQ, &nod1, &nod2); regfree(&nod1); nod2.type = typ(TIND, t); diff --git a/src/cmd/6c/doc.go b/src/cmd/6c/doc.go index 249a8ed80..e0a22e78b 100644 --- a/src/cmd/6c/doc.go +++ b/src/cmd/6c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 6c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the x86-64, referred to by these tools as amd64. */ -package documentation +package main diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index b0081abb5..d1133ee21 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -292,6 +292,7 @@ void gbranch(int); void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c @@ -366,8 +367,6 @@ int BtoF(int32); #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - /* * bound */ diff --git a/src/cmd/6c/list.c b/src/cmd/6c/list.c index 4293203c0..b5a60ac9a 100644 --- a/src/cmd/6c/list.c +++ b/src/cmd/6c/list.c @@ -93,7 +93,7 @@ Pconv(Fmt *fp) break; default: - sprint(str, "(%L) %A %D,%lD", + sprint(str, "(%L) %A %D,%D", p->lineno, p->as, &p->from, &p->to); break; } @@ -120,13 +120,12 @@ Dconv(Fmt *fp) i = a->type; if(fp->flags & FmtLong) { - if(i != D_CONST) { + if(i == D_CONST) + sprint(str, "$%lld-%lld", a->offset&0xffffffffLL, a->offset>>32); + else { // ATEXT dst is not constant sprint(str, "!!%D", a); - goto brk; } - sprint(str, "$%lld-%lld", a->offset&0xffffffffLL, - (a->offset>>32)&0xffffffffLL); goto brk; } @@ -138,7 +137,6 @@ Dconv(Fmt *fp) goto brk; } switch(i) { - default: if(a->offset) sprint(str, "$%lld,%R", a->offset, i); @@ -151,7 +149,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%lld(PC)", a->offset-pc); + sprint(str, "%lld", a->offset); break; case D_EXTERN: @@ -159,24 +157,21 @@ Dconv(Fmt *fp) break; case D_STATIC: - sprint(str, "%s<>+%lld(SB)", a->sym->name, - a->offset); + sprint(str, "%s<>+%lld(SB)", a->sym->name, a->offset); break; case D_AUTO: - if(a->sym) { + if(a->sym) sprint(str, "%s+%lld(SP)", a->sym->name, a->offset); - break; - } - sprint(str, "%lld(SP)", a->offset); + else + sprint(str, "%lld(SP)", a->offset); break; case D_PARAM: - if(a->sym) { + if(a->sym) sprint(str, "%s+%lld(FP)", a->sym->name, a->offset); - break; - } - sprint(str, "%lld(FP)", a->offset); + else + sprint(str, "%lld(FP)", a->offset); break; case D_CONST: @@ -210,7 +205,7 @@ conv: char* regstr[] = { - "AL", /* [D_AL] */ + "AL", /* [D_AL] */ "CL", "DL", "BL", @@ -227,7 +222,7 @@ char* regstr[] = "R14B", "R15B", - "AX", /* [D_AX] */ + "AX", /* [D_AX] */ "CX", "DX", "BX", @@ -249,7 +244,7 @@ char* regstr[] = "DH", "BH", - "F0", /* [D_F0] */ + "F0", /* [D_F0] */ "F1", "F2", "F3", @@ -284,20 +279,20 @@ char* regstr[] = "X14", "X15", - "CS", /* [D_CS] */ + "CS", /* [D_CS] */ "SS", "DS", "ES", "FS", "GS", - "GDTR", /* [D_GDTR] */ - "IDTR", /* [D_IDTR] */ - "LDTR", /* [D_LDTR] */ - "MSW", /* [D_MSW] */ - "TASK", /* [D_TASK] */ + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ - "CR0", /* [D_CR] */ + "CR0", /* [D_CR] */ "CR1", "CR2", "CR3", @@ -314,7 +309,7 @@ char* regstr[] = "CR14", "CR15", - "DR0", /* [D_DR] */ + "DR0", /* [D_DR] */ "DR1", "DR2", "DR3", @@ -323,7 +318,7 @@ char* regstr[] = "DR6", "DR7", - "TR0", /* [D_TR] */ + "TR0", /* [D_TR] */ "TR1", "TR2", "TR3", @@ -332,7 +327,7 @@ char* regstr[] = "TR6", "TR7", - "NONE", /* [D_NONE] */ + "NONE", /* [D_NONE] */ }; int diff --git a/src/cmd/6c/mul.c b/src/cmd/6c/mul.c index ab6883e7a..510edc05c 100644 --- a/src/cmd/6c/mul.c +++ b/src/cmd/6c/mul.c @@ -35,17 +35,17 @@ typedef struct Mparam Mparam; struct Malg { - char vals[10]; + schar vals[10]; }; struct Mparam { uint32 value; - char alg; + schar alg; char neg; char shift; char arg; - char off; + schar off; }; static Mparam multab[32]; @@ -101,7 +101,7 @@ mulparam(uint32 m, Mparam *mp) { int c, i, j, n, o, q, s; int bc, bi, bn, bo, bq, bs, bt; - char *p; + schar *p; int32 u; uint32 t; diff --git a/src/cmd/6c/peep.c b/src/cmd/6c/peep.c index 8b82adbf5..c648d8c00 100644 --- a/src/cmd/6c/peep.c +++ b/src/cmd/6c/peep.c @@ -330,20 +330,7 @@ subprop(Reg *r0) case AIMULW: if(p->to.type != D_NONE) break; - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: + goto giveup; case AROLB: case AROLL: @@ -369,6 +356,23 @@ subprop(Reg *r0) case ASHRL: case ASHRQ: case ASHRW: + if(p->from.type == D_CONST) + break; + goto giveup; + + case ADIVB: + case ADIVL: + case ADIVQ: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVQ: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULQ: + case AMULW: case AREP: case AREPN: @@ -383,6 +387,8 @@ subprop(Reg *r0) case AMOVSB: case AMOVSL: case AMOVSQ: + case AMOVQL: + giveup: return 0; case AMOVL: @@ -581,6 +587,7 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -695,6 +702,11 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPB: case ACMPQ: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: + case ACOMISD: case ACOMISS: case AUCOMISD: diff --git a/src/cmd/6c/reg.c b/src/cmd/6c/reg.c index 996128f75..e40e6c8f0 100644 --- a/src/cmd/6c/reg.c +++ b/src/cmd/6c/reg.c @@ -30,6 +30,8 @@ #include "gc.h" +static void fixjmp(Reg*); + Reg* rega(void) { @@ -185,6 +187,10 @@ regopt(Prog *p) case ACMPL: case ACMPQ: case ACMPW: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: case ACOMISS: case ACOMISD: case AUCOMISS: @@ -211,6 +217,7 @@ regopt(Prog *p) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -438,6 +445,12 @@ regopt(Prog *p) } /* + * pass 2.1 + * fix jumps + */ + fixjmp(firstr); + + /* * pass 2.5 * find looping structure */ @@ -1384,3 +1397,126 @@ BtoF(int32 b) return 0; return bitno(b) - 16 + FREGMIN; } + +/* what instruction does a JMP to p eventually land on? */ +static Reg* +chasejmp(Reg *r, int *jmploop) +{ + int n; + + n = 0; + for(; r; r=r->s2) { + if(r->prog->as != AJMP || r->prog->to.type != D_BRANCH) + break; + if(++n > 10) { + *jmploop = 1; + break; + } + } + return r; +} + +/* mark all code reachable from firstp as alive */ +static void +mark(Reg *firstr) +{ + Reg *r; + Prog *p; + + for(r=firstr; r; r=r->link) { + if(r->active) + break; + r->active = 1; + p = r->prog; + if(p->as != ACALL && p->to.type == D_BRANCH) + mark(r->s2); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) + break; + } +} + +/* + * the code generator depends on being able to write out JMP + * instructions that it can jump to now but fill in later. + * the linker will resolve them nicely, but they make the code + * longer and more difficult to follow during debugging. + * remove them. + */ +static void +fixjmp(Reg *firstr) +{ + int jmploop; + Reg *r; + Prog *p; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + for(r=firstr; r; r=r->link) { + p = r->prog; + if(debug['R'] && debug['v']) + print("%04d %P\n", r->pc, p); + if(p->as != ACALL && p->to.type == D_BRANCH && r->s2 && r->s2->prog->as == AJMP) { + r->s2 = chasejmp(r->s2, &jmploop); + p->to.offset = r->s2->pc; + if(debug['R'] && debug['v']) + print("->%P\n", p); + } + r->active = 0; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstr); + + // pass 3: delete dead code (mostly JMPs). + for(r=firstr; r; r=r->link) { + if(!r->active) { + p = r->prog; + if(p->link == P && p->as == ARET && r->p1 && r->p1->prog->as != ARET) { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. + } else { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if(!jmploop) { + for(r=firstr; r; r=r->link) { + p = r->prog; + if(p->as == AJMP && p->to.type == D_BRANCH && r->s2 == r->link) { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // fix back pointers. + for(r=firstr; r; r=r->link) { + r->p2 = R; + r->p2link = R; + } + for(r=firstr; r; r=r->link) { + if(r->s2) { + r->p2link = r->s2->p2; + r->s2->p2 = r; + } + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } +} + diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index 42045f8fa..2402a020d 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -126,7 +126,10 @@ xcom(Node *n) break; case ONAME: - n->addable = 10; + if(flag_largemodel) + n->addable = 9; + else + n->addable = 10; if(n->class == CPARAM || n->class == CAUTO) n->addable = 11; break; diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index f16d0f78a..58d6d5129 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -247,18 +247,12 @@ outcode(void) Binit(&b, f, OWRITE); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&b, "\n"); Bprint(&b, "$$ // exports\n\n"); Bprint(&b, "$$ // local types\n\n"); - Bprint(&b, "$$ // dynimport\n"); - for(i=0; i<ndynimp; i++) - Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path); - Bprint(&b, "\n$$ // dynexport\n"); - for(i=0; i<ndynexp; i++) - Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote); + Bprint(&b, "$$ // cgo\n"); + Bprint(&b, "%s", fmtstrflush(&pragcgobuf)); Bprint(&b, "\n$$\n\n"); } Bprint(&b, "!\n"); @@ -339,12 +333,38 @@ outhist(Biobuf *b) char *p, *q, *op, c; Prog pg; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + tofree = nil; pg = zprog; pg.as = AHISTORY; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -400,6 +420,11 @@ outhist(Biobuf *b) Bputc(b, pg.lineno>>24); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -591,8 +616,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) int32 maxround(int32 max, int32 v) { - v += SZ_VLONG-1; + v = xround(v, SZ_VLONG); if(v > max) - max = xround(v, SZ_VLONG); + return v; return max; } diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index 2cb8c1585..364b189f2 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -809,7 +809,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TCHAR): case CASE( TLONG, TCHAR): case CASE( TULONG, TCHAR): - case CASE( TIND, TCHAR): case CASE( TCHAR, TUCHAR): case CASE( TUCHAR, TUCHAR): @@ -819,7 +818,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TUCHAR): case CASE( TLONG, TUCHAR): case CASE( TULONG, TUCHAR): - case CASE( TIND, TUCHAR): case CASE( TSHORT, TSHORT): case CASE( TUSHORT,TSHORT): @@ -827,7 +825,6 @@ gmove(Node *f, Node *t) case CASE( TUINT, TSHORT): case CASE( TLONG, TSHORT): case CASE( TULONG, TSHORT): - case CASE( TIND, TSHORT): case CASE( TSHORT, TUSHORT): case CASE( TUSHORT,TUSHORT): @@ -835,42 +832,26 @@ gmove(Node *f, Node *t) case CASE( TUINT, TUSHORT): case CASE( TLONG, TUSHORT): case CASE( TULONG, TUSHORT): - case CASE( TIND, TUSHORT): case CASE( TINT, TINT): case CASE( TUINT, TINT): case CASE( TLONG, TINT): case CASE( TULONG, TINT): - case CASE( TIND, TINT): case CASE( TINT, TUINT): case CASE( TUINT, TUINT): case CASE( TLONG, TUINT): case CASE( TULONG, TUINT): - case CASE( TIND, TUINT): - - case CASE( TUINT, TIND): - case CASE( TVLONG, TUINT): - case CASE( TVLONG, TULONG): - case CASE( TUVLONG, TUINT): - case CASE( TUVLONG, TULONG): *****/ a = AMOVL; break; - case CASE( TVLONG, TCHAR): - case CASE( TVLONG, TSHORT): - case CASE( TVLONG, TINT): - case CASE( TVLONG, TLONG): - case CASE( TUVLONG, TCHAR): - case CASE( TUVLONG, TSHORT): - case CASE( TUVLONG, TINT): - case CASE( TUVLONG, TLONG): + case CASE( TINT, TIND): case CASE( TINT, TVLONG): case CASE( TINT, TUVLONG): - case CASE( TLONG, TVLONG): - case CASE( TINT, TIND): case CASE( TLONG, TIND): + case CASE( TLONG, TVLONG): + case CASE( TLONG, TUVLONG): a = AMOVLQSX; if(f->op == OCONST) { f->vconst &= (uvlong)0xffffffffU; @@ -886,22 +867,53 @@ gmove(Node *f, Node *t) case CASE( TULONG, TVLONG): case CASE( TULONG, TUVLONG): case CASE( TULONG, TIND): - a = AMOVL; /* same effect as AMOVLQZX */ + a = AMOVLQZX; if(f->op == OCONST) { f->vconst &= (uvlong)0xffffffffU; a = AMOVQ; } break; + + case CASE( TIND, TCHAR): + case CASE( TIND, TUCHAR): + case CASE( TIND, TSHORT): + case CASE( TIND, TUSHORT): + case CASE( TIND, TINT): + case CASE( TIND, TUINT): + case CASE( TIND, TLONG): + case CASE( TIND, TULONG): + case CASE( TVLONG, TCHAR): + case CASE( TVLONG, TUCHAR): + case CASE( TVLONG, TSHORT): + case CASE( TVLONG, TUSHORT): + case CASE( TVLONG, TINT): + case CASE( TVLONG, TUINT): + case CASE( TVLONG, TLONG): + case CASE( TVLONG, TULONG): + case CASE( TUVLONG, TCHAR): + case CASE( TUVLONG, TUCHAR): + case CASE( TUVLONG, TSHORT): + case CASE( TUVLONG, TUSHORT): + case CASE( TUVLONG, TINT): + case CASE( TUVLONG, TUINT): + case CASE( TUVLONG, TLONG): + case CASE( TUVLONG, TULONG): + a = AMOVQL; + if(f->op == OCONST) { + f->vconst &= (int)0xffffffffU; + a = AMOVL; + } + break; + case CASE( TIND, TIND): case CASE( TIND, TVLONG): - case CASE( TVLONG, TVLONG): - case CASE( TUVLONG, TVLONG): - case CASE( TVLONG, TUVLONG): - case CASE( TUVLONG, TUVLONG): case CASE( TIND, TUVLONG): case CASE( TVLONG, TIND): + case CASE( TVLONG, TVLONG): + case CASE( TVLONG, TUVLONG): case CASE( TUVLONG, TIND): - case CASE( TIND, TIND): + case CASE( TUVLONG, TVLONG): + case CASE( TUVLONG, TUVLONG): a = AMOVQ; break; @@ -1348,6 +1360,16 @@ gopcode(int o, Type *ty, Node *f, Node *t) a = ASALQ; break; + case OROTL: + a = AROLL; + if(et == TCHAR || et == TUCHAR) + a = AROLB; + if(et == TSHORT || et == TUSHORT) + a = AROLW; + if(et == TVLONG || et == TUVLONG || et == TIND) + a = AROLQ; + break; + case OFUNC: a = ACALL; break; @@ -1490,6 +1512,18 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APREFETCHNTA, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 00334e71b..a51c0ca58 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -33,9 +33,26 @@ cgen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; - // inline slices - if(cgen_inline(n, res)) + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); goto ret; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); + goto ret; + } if(n->ullman >= UINF) { if(n->op == OINDREG) @@ -174,7 +191,7 @@ cgen(Node *n, Node *res) switch(n->op) { default: dump("cgen", n); - fatal("cgen: unknown op %N", n); + fatal("cgen: unknown op %+hN", n); break; // these call bgen to get a bool value @@ -187,12 +204,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AJMP, T); + p3 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); goto ret; @@ -229,17 +246,41 @@ cgen(Node *n, Node *res) case OADD: case OMUL: a = optoas(n->op, nl->type); - if(a != AIMULB) - goto sbop; - cgen_bmul(n->op, nl, nr, res); - break; + if(a == AIMULB) { + cgen_bmul(n->op, nl, nr, res); + break; + } + goto sbop; // asymmetric binary case OSUB: a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + case OCONV: + if(n->type->width > nl->type->width) { + // If loading from memory, do conversion during load, + // so as to avoid use of 8-bit register in, say, int(*byteptr). + switch(nl->op) { + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: + igen(nl, &n1, res); + regalloc(&n2, n->type, res); + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n2); + regfree(&n1); + goto ret; + } + } + regalloc(&n1, nl->type, res); regalloc(&n2, n->type, &n1); cgen(nl, &n1); @@ -273,18 +314,18 @@ cgen(Node *n, Node *res) case OLEN: if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { - // map and chan have len in the first 32-bit word. + // map and chan have len in the first int-sized word. // a zero pointer means zero length regalloc(&n1, types[tptr], res); cgen(nl, &n1); nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); n2 = n1; n2.op = OINDREG; - n2.type = types[TINT32]; + n2.type = types[simtype[TINT]]; gmove(&n2, &n1); patch(p1, pc); @@ -297,7 +338,7 @@ cgen(Node *n, Node *res) // both slice and string have len one pointer into the struct. // a zero pointer means zero length igen(nl, &n1, res); - n1.type = types[TUINT32]; + n1.type = types[simtype[TUINT]]; n1.xoffset += Array_nel; gmove(&n1, res); regfree(&n1); @@ -308,19 +349,19 @@ cgen(Node *n, Node *res) case OCAP: if(istype(nl->type, TCHAN)) { - // chan has cap in the second 32-bit word. + // chan has cap in the second int-sized word. // a zero pointer means zero length regalloc(&n1, types[tptr], res); cgen(nl, &n1); nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); n2 = n1; n2.op = OINDREG; - n2.xoffset = 4; - n2.type = types[TINT32]; + n2.xoffset = widthint; + n2.type = types[simtype[TINT]]; gmove(&n2, &n1); patch(p1, pc); @@ -331,7 +372,7 @@ cgen(Node *n, Node *res) } if(isslice(nl->type)) { igen(nl, &n1, res); - n1.type = types[TUINT32]; + n1.type = types[simtype[TUINT]]; n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); @@ -365,18 +406,53 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; } - cgen_div(n->op, nl, nr, res); + + if(nl->ullman >= nr->ullman) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + cgen_div(n->op, &n1, nr, res); + regfree(&n1); + } else { + if(!smallintconst(nr)) { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } else { + n2 = *nr; + } + cgen_div(n->op, nl, &n2, res); + if(n2.op != OLITERAL) + regfree(&n2); + } break; case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + case OLROT: + cgen_shift(n->op, n->bounded, nl, nr, res); break; } goto ret; sbop: // symmetric binary - if(nl->ullman < nr->ullman) { + /* + * put simplest on right - we'll generate into left + * and then adjust it using the computation of right. + * constants and variables have the same ullman + * count, so look for constants specially. + * + * an integer constant we can use as an immediate + * is simpler than a variable - we can use the immediate + * in the adjustment instruction directly - so it goes + * on the right. + * + * other constants, like big integers or floating point + * constants, require a mov into a register, so those + * might as well go on the left, so we can reuse that + * register for the computation. + */ + if(nl->ullman < nr->ullman || + (nl->ullman == nr->ullman && + (smallintconst(nl) || (nr->op == OLITERAL && !smallintconst(nr))))) { r = nl; nl = nr; nr = r; @@ -386,7 +462,13 @@ abop: // asymmetric binary if(nl->ullman >= nr->ullman) { regalloc(&n1, nl->type, res); cgen(nl, &n1); - + /* + * This generates smaller code - it avoids a MOV - but it's + * easily 10% slower due to not being able to + * optimize/manipulate the move. + * To see, run: go test -bench . crypto/md5 + * with and without. + * if(sudoaddable(a, nr, &addr)) { p1 = gins(a, N, &n1); p1->from = addr; @@ -395,18 +477,30 @@ abop: // asymmetric binary regfree(&n1); goto ret; } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); + * + */ + + if(smallintconst(nr)) + n2 = *nr; + else { + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + } } else { - regalloc(&n2, nr->type, res); - cgen(nr, &n2); + if(smallintconst(nr)) + n2 = *nr; + else { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } regalloc(&n1, nl->type, N); cgen(nl, &n1); } gins(a, &n2, &n1); gmove(&n1, res); regfree(&n1); - regfree(&n2); + if(n2.op != OLITERAL) + regfree(&n2); goto ret; uop: // unary @@ -422,93 +516,142 @@ ret: } /* - * generate: - * res = &n; + * allocate a register in res and generate + * newreg = &n + * The caller must call regfree(a). */ void -agen(Node *n, Node *res) +cgenr(Node *n, Node *a, Node *res) +{ + Node n1; + + if(debug['g']) + dump("cgenr-n", n); + + if(isfat(n->type)) + fatal("cgenr on fat node"); + + if(n->addable) { + regalloc(a, n->type, res); + gmove(n, a); + return; + } + + switch(n->op) { + case ONAME: + case ODOT: + case ODOTPTR: + case OINDEX: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, n->type, res); + cgen(n, a); + break; + } +} + +/* + * allocate a register in res and generate + * res = &n + */ +void +agenr(Node *n, Node *a, Node *res) { Node *nl, *nr; - Node n1, n2, n3, tmp, n4, n5; + Node n1, n2, n3, n4, n5, tmp, tmp2, nlen; Prog *p1; + Type *t; uint32 w; uint64 v; - Type *t; + int freelen; if(debug['g']) { - dump("\nagen-res", res); - dump("agen-r", n); - } - if(n == N || n->type == T) - return; - - while(n->op == OCONVNOP) - n = n->left; - - if(n->addable) { - regalloc(&n1, types[tptr], res); - gins(ALEAQ, n, &n1); - gmove(&n1, res); - regfree(&n1); - goto ret; + dump("\nagenr-n", n); } nl = n->left; nr = n->right; switch(n->op) { - default: - fatal("agen: unknown op %N", n); - break; - + case ODOT: + case ODOTPTR: + case OCALLFUNC: case OCALLMETH: - cgen_callmeth(n, 0); - cgen_aret(n, res); - break; - case OCALLINTER: - cgen_callinter(n, res, 0); - cgen_aret(n, res); + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + agen(&n1, a); + regfree(&n1); break; - case OCALLFUNC: - cgen_call(n, 0); - cgen_aret(n, res); + case OIND: + cgenr(n->left, a, res); break; case OINDEX: + freelen = 0; w = n->type->width; + // Generate the non-addressable child first. if(nr->addable) goto irad; if(nl->addable) { - if(!isconst(nr, CTINT)) { - regalloc(&n1, nr->type, N); - cgen(nr, &n1); - } + cgenr(nr, &n1, N); if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(isfixedarray(nl->type)) { + agenr(nl, &n3, res); + } else { + igen(nl, &nlen, res); + freelen = 1; + nlen.type = types[tptr]; + nlen.xoffset += Array_array; + regalloc(&n3, types[tptr], res); + gmove(&nlen, &n3); + nlen.type = types[simtype[TUINT]]; + nlen.xoffset += Array_nel-Array_array; + } } goto index; } tempname(&tmp, nr->type); cgen(nr, &tmp); nr = &tmp; - irad: if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); + if(isfixedarray(nl->type)) { + agenr(nl, &n3, res); + } else { + if(!nl->addable) { + // igen will need an addressable node. + tempname(&tmp2, nl->type); + cgen(nl, &tmp2); + nl = &tmp2; + } + igen(nl, &nlen, res); + freelen = 1; + nlen.type = types[tptr]; + nlen.xoffset += Array_array; + regalloc(&n3, types[tptr], res); + gmove(&nlen, &n3); + nlen.type = types[simtype[TUINT]]; + nlen.xoffset += Array_nel-Array_array; + } } if(!isconst(nr, CTINT)) { - regalloc(&n1, nr->type, N); - cgen(nr, &n1); + cgenr(nr, &n1, N); } goto index; index: // &a is in &n3 (allocated in res) // i is in &n1 (if not constant) + // len(a) is in nlen (if needed) // w is width // explicit check for nil if array is large enough @@ -529,29 +672,26 @@ agen(Node *n, Node *res) fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; - nodconst(&n2, types[TUINT32], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + if(!debug['B'] && !n->bounded) { + nodconst(&n2, types[simtype[TUINT]], v); + if(smallintconst(nr)) { + gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &n2); + } else { + regalloc(&tmp, types[simtype[TUINT]], N); + gmove(&n2, &tmp); + gins(optoas(OCMP, types[simtype[TUINT]]), &nlen, &tmp); + regfree(&tmp); + } + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } - - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); + regfree(&nlen); } if (v*w != 0) ginscon(optoas(OADD, types[tptr]), v*w, &n3); - gmove(&n3, res); - regfree(&n3); + *a = n3; break; } @@ -564,32 +704,32 @@ agen(Node *n, Node *res) gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - n5.op = OXXX; - t = types[TUINT32]; + t = types[simtype[TUINT]]; if(is64(nr->type)) t = types[TUINT64]; if(isconst(nl, CTSTR)) { - nodconst(&n1, t, nl->val.u.sval->len); + nodconst(&nlen, t, nl->val.u.sval->len); } else if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[TUINT32]; - n1.xoffset = Array_nel; if(is64(nr->type)) { regalloc(&n5, t, N); - gmove(&n1, &n5); - n1 = n5; + gmove(&nlen, &n5); + regfree(&nlen); + nlen = n5; } } else { - nodconst(&n1, t, nl->type->bound); + nodconst(&nlen, t, nl->type->bound); + if(!smallintconst(&nlen)) { + regalloc(&n5, t, N); + gmove(&nlen, &n5); + nlen = n5; + freelen = 1; + } } - gins(optoas(OCMP, t), &n2, &n1); - p1 = gbranch(optoas(OLT, t), T); - if(n5.op != OXXX) - regfree(&n5); - ginscall(panicindex, 0); + gins(optoas(OCMP, t), &n2, &nlen); + p1 = gbranch(optoas(OLT, t), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } @@ -597,19 +737,15 @@ agen(Node *n, Node *res) regalloc(&n3, types[tptr], res); p1 = gins(ALEAQ, N, &n3); datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); - p1->from.scale = 1; - p1->from.index = n2.val.u.reg; + if(flag_largemodel) { + gins(AADDQ, &n2, &n3); + } else { + p1->from.scale = 1; + p1->from.index = n2.val.u.reg; + } goto indexdone; } - if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); - } - if(w == 0) { // nothing to do } else if(w == 1 || w == 2 || w == 4 || w == 8) { @@ -623,9 +759,103 @@ agen(Node *n, Node *res) } indexdone: - gmove(&n3, res); + *a = n3; regfree(&n2); - regfree(&n3); + if(freelen) + regfree(&nlen); + break; + + default: + regalloc(a, types[tptr], res); + agen(n, a); + break; + } +} + +/* + * generate: + * res = &n; + */ +void +agen(Node *n, Node *res) +{ + Node *nl, *nr; + Node n1, n2; + + if(debug['g']) { + dump("\nagen-res", res); + dump("agen-r", n); + } + if(n == N || n->type == T) + return; + + while(n->op == OCONVNOP) + n = n->left; + + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(ALEAQ, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + if(n->addable) { + regalloc(&n1, types[tptr], res); + gins(ALEAQ, n, &n1); + gmove(&n1, res); + regfree(&n1); + goto ret; + } + + nl = n->left; + nr = n->right; + USED(nr); + + switch(n->op) { + default: + fatal("agen: unknown op %+hN", n); + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + cgen_aret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_aret(n, res); + break; + + case OCALLFUNC: + cgen_call(n, 0); + cgen_aret(n, res); + break; + + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + + case OINDEX: + agenr(n, &n1, res); + gmove(&n1, res); + regfree(&n1); break; case ONAME: @@ -692,7 +922,11 @@ igen(Node *n, Node *a, Node *res) { Type *fp; Iter flist; - + Node n1; + + if(debug['g']) { + dump("\nigen-n", n); + } switch(n->op) { case ONAME: if((n->class&PHEAP) || n->class == PPARAMREF) @@ -700,9 +934,53 @@ igen(Node *n, Node *a, Node *res) *a = *n; return; + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != D_SP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + cgenr(n->left, a, res); + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + n1 = *a; + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gins(ATESTB, nodintconst(0), &n1); + } + } + a->op = OINDREG; + a->xoffset += n->xoffset; + a->type = n->type; + return; + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + switch(n->op) { + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } fp = structfirst(&flist, getoutarg(n->left->type)); - cgen_call(n, 0); memset(a, 0, sizeof *a); a->op = OINDREG; a->val.u.reg = D_SP; @@ -710,10 +988,34 @@ igen(Node *n, Node *a, Node *res) a->xoffset = fp->width; a->type = n->type; return; + + case OINDEX: + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. + if(isfixedarray(n->left->type) || + (isptr[n->left->type->etype] && isfixedarray(n->left->left->type))) + if(isconst(n->right, CTINT)) { + // Compute &a. + if(!isptr[n->left->type->etype]) + igen(n->left, a, res); + else { + igen(n->left, &n1, res); + regalloc(a, types[tptr], res); + gmove(&n1, a); + regfree(&n1); + a->op = OINDREG; + } + + // Compute &a[i] as &a + i*width. + a->type = n->type; + a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width; + return; + } } - - regalloc(a, types[tptr], res); - agen(n, a); + + agenr(n, a, res); a->op = OINDREG; a->type = n->type; } @@ -723,7 +1025,7 @@ igen(Node *n, Node *a, Node *res) * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *l, *r; @@ -765,14 +1067,14 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); regfree(&n1); goto ret; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, likely), to); goto ret; case ONAME: @@ -783,7 +1085,7 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); goto ret; case OANDAND: @@ -791,12 +1093,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AJMP, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); patch(p1, to); patch(p2, pc); goto ret; @@ -806,8 +1108,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); goto ret; case OEQ: @@ -830,7 +1132,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); goto ret; case OEQ: @@ -843,14 +1145,14 @@ bgen(Node *n, int true, Prog *to) if(!true) { if(isfloat[nr->type->etype]) { // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); ll = n->ninit; // avoid re-genning ninit n->ninit = nil; - bgen(n, 1, p2); + bgen(n, 1, -likely, p2); n->ninit = ll; - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, 0), to); patch(p2, pc); goto ret; } @@ -865,47 +1167,41 @@ bgen(Node *n, int true, Prog *to) nl = nr; nr = r; } - + if(isslice(nl->type)) { - // only valid to cmp darray to literal nil + // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { - yyerror("illegal array comparison"); + yyerror("illegal slice comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - n2.type = types[tptr]; + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } if(isinter(nl->type)) { - // front end shold only leave cmp to literal nil + // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { yyerror("illegal interface comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; + igen(nl, &n1, N); + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -931,7 +1227,7 @@ bgen(Node *n, int true, Prog *to) if(smallintconst(nr)) { gins(optoas(OCMP, nr->type), &n1, nr); - patch(gbranch(optoas(a, nr->type), nr->type), to); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); regfree(&n1); break; } @@ -953,18 +1249,18 @@ bgen(Node *n, int true, Prog *to) if(isfloat[nr->type->etype] && (n->op == OEQ || n->op == ONE)) { if(n->op == OEQ) { // neither NE nor P - p1 = gbranch(AJNE, T); - p2 = gbranch(AJPS, T); - patch(gbranch(AJMP, T), to); + p1 = gbranch(AJNE, T, -likely); + p2 = gbranch(AJPS, T, -likely); + patch(gbranch(AJMP, T, 0), to); patch(p1, pc); patch(p2, pc); } else { // either NE or P - patch(gbranch(AJNE, T), to); - patch(gbranch(AJPS, T), to); + patch(gbranch(AJNE, T, likely), to); + patch(gbranch(AJPS, T, likely), to); } } else - patch(gbranch(optoas(a, nr->type), nr->type), to); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); regfree(&n1); regfree(&n2); break; @@ -1036,8 +1332,8 @@ stkof(Node *n) void sgen(Node *n, Node *ns, int64 w) { - Node nodl, nodr, oldl, oldr, cx, oldcx, tmp; - int32 c, q, odst, osrc; + Node nodl, nodr, nodsi, noddi, cx, oldcx, tmp; + vlong c, q, odst, osrc; if(debug['g']) { print("\nsgen w=%lld\n", w); @@ -1051,9 +1347,9 @@ sgen(Node *n, Node *ns, int64 w) if(w < 0) fatal("sgen copy %lld", w); - if(w == 16) - if(componentgen(n, ns)) - return; + // Avoid taking the address for simple enough types. + if(componentgen(n, ns)) + return; if(w == 0) { // evaluate side effects only @@ -1080,22 +1376,18 @@ sgen(Node *n, Node *ns, int64 w) } if(n->ullman >= ns->ullman) { - savex(D_SI, &nodr, &oldr, N, types[tptr]); - agen(n, &nodr); - - regalloc(&nodr, types[tptr], &nodr); // mark nodr as live - savex(D_DI, &nodl, &oldl, N, types[tptr]); - agen(ns, &nodl); - regfree(&nodr); + agenr(n, &nodr, N); + agenr(ns, &nodl, N); } else { - savex(D_DI, &nodl, &oldl, N, types[tptr]); - agen(ns, &nodl); - - regalloc(&nodl, types[tptr], &nodl); // mark nodl as live - savex(D_SI, &nodr, &oldr, N, types[tptr]); - agen(n, &nodr); - regfree(&nodl); + agenr(ns, &nodl, N); + agenr(n, &nodr, N); } + nodreg(&noddi, types[tptr], D_DI); + nodreg(&nodsi, types[tptr], D_SI); + gmove(&nodl, &noddi); + gmove(&nodr, &nodsi); + regfree(&nodl); + regfree(&nodr); c = w % 8; // bytes q = w / 8; // quads @@ -1152,9 +1444,6 @@ sgen(Node *n, Node *ns, int64 w) } } - - restx(&nodl, &oldl); - restx(&nodr, &oldr); restx(&cx, &oldcx); } @@ -1175,15 +1464,21 @@ cadable(Node *n) } /* - * copy a structure component by component + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * Small structs or arrays with elements of basic type are + * also supported. + * nr is N when assigning a zero value. * return 1 if can do, 0 if cant. - * nr is N for copy zero */ int componentgen(Node *nr, Node *nl) { Node nodl, nodr; + Type *t; int freel, freer; + vlong fldcount; + vlong loffset, roffset; freel = 0; freer = 0; @@ -1193,8 +1488,33 @@ componentgen(Node *nr, Node *nl) goto no; case TARRAY: - if(!isslice(nl->type)) + t = nl->type; + + // Slices are ok. + if(isslice(t)) + break; + // Small arrays are ok. + if(t->bound > 0 && t->bound <= 3 && !isfat(t->type)) + break; + + goto no; + + case TSTRUCT: + // Small structs with non-fat types are ok. + // Zero-sized structs are treated separately elsewhere. + fldcount = 0; + for(t=nl->type->type; t; t=t->down) { + if(isfat(t->type)) + goto no; + if(t->etype != TFIELD) + fatal("componentgen: not a TFIELD: %lT", t); + fldcount++; + } + if(fldcount == 0 || fldcount > 3) goto no; + + break; + case TSTRING: case TINTER: break; @@ -1218,9 +1538,23 @@ componentgen(Node *nr, Node *nl) switch(nl->type->etype) { case TARRAY: - if(!isslice(nl->type)) - goto no; + // componentgen for arrays. + t = nl->type; + if(!isslice(t)) { + nodl.type = t->type; + nodr.type = nodl.type; + for(fldcount=0; fldcount < t->bound; fldcount++) { + if(nr == N) + clearslim(&nodl); + else + gmove(&nodr, &nodl); + nodl.xoffset += t->type->width; + nodr.xoffset += t->type->width; + } + goto yes; + } + // componentgen for slices. nodl.xoffset += Array_array; nodl.type = ptrto(nl->type->type); @@ -1232,7 +1566,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1242,7 +1576,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_cap-Array_nel; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_cap-Array_nel; @@ -1265,7 +1599,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1300,7 +1634,27 @@ componentgen(Node *nr, Node *nl) goto yes; case TSTRUCT: - goto no; + loffset = nodl.xoffset; + roffset = nodr.xoffset; + // funarg structs may not begin at offset zero. + if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type) + loffset -= nl->type->type->width; + if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type) + roffset -= nr->type->type->width; + + for(t=nl->type->type; t; t=t->down) { + nodl.xoffset = loffset + t->width; + nodl.type = t->type; + + if(nr == N) + clearslim(&nodl); + else { + nodr.xoffset = roffset + t->width; + nodr.type = nodl.type; + gmove(&nodr, &nodl); + } + } + goto yes; } no: diff --git a/src/cmd/6g/doc.go b/src/cmd/6g/doc.go index 64f1d2ba9..07b2818da 100644 --- a/src/cmd/6g/doc.go +++ b/src/cmd/6g/doc.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. +// +build ignore + /* 6g is the version of the gc compiler for the x86-64. @@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64. It reads .go files and outputs .6 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index b03ac1ed6..526c04c06 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -17,8 +17,8 @@ vlong MAXWIDTH = 1LL<<50; */ Typedef typedefs[] = { - "int", TINT, TINT32, - "uint", TUINT, TUINT32, + "int", TINT, TINT64, + "uint", TUINT, TUINT64, "uintptr", TUINTPTR, TUINT64, 0 }; @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 8; + widthint = 8; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 47a540082..ceb6a2caa 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -14,19 +14,22 @@ typedef struct Addr Addr; struct Addr { vlong offset; - double dval; - Prog* branch; - char sval[NSNAME]; + + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* gotype; Sym* sym; Node* node; - int width; + int64 width; uchar type; uchar index; uchar etype; uchar scale; /* doubles as width in DATA op */ - uchar pun; /* dont register variable */ }; #define A ((Addr*)0) @@ -58,7 +61,7 @@ EXTERN Node* throwreturn; extern vlong unmappedzero; /* - * gen.c + * ggen.c */ void compile(Node*); void proglist(void); @@ -71,29 +74,31 @@ void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_div(int, Node*, Node*, Node*); void cgen_bmul(int, Node*, Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); void cgen_dcl(Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); int gen_as_init(Node*); +void clearslim(Node*); /* - * cgen + * cgen.c */ void agen(Node*, Node*); +void agenr(Node*, Node*, Node*); +void cgenr(Node*, Node*, Node*); void igen(Node*, Node*, Node*); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -int cgen_inline(Node*, Node*); void restx(Node*, Node*); void savex(int, Node*, Node*, Node*, Type*); int componentgen(Node*, Node*); @@ -103,9 +108,8 @@ int componentgen(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); @@ -126,9 +130,9 @@ Plist* newplist(void); int isfat(Type*); void sudoclean(void); int sudoaddable(int, Node*, Addr*); -void afunclit(Addr*); -void datagostring(Strlit*, Addr*); +void afunclit(Addr*, Node*); void nodfconst(Node*, Type*, Mpflt*); +void gtrack(Sym*); /* * cplx.c @@ -136,12 +140,12 @@ void nodfconst(Node*, Type*, Mpflt*); int complexop(Node*, Node*); void complexmove(Node*, Node*); void complexgen(Node*, Node*); -void complexbool(int, Node*, Node*, int, Prog*); /* * gobj.c */ void datastring(char*, int, Addr*); +void datagostring(Strlit*, Addr*); /* * list.c diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 02e67d6d4..23bb5093f 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -25,6 +25,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.type == D_AUTO && p->from.node) p->from.node->used = 1; @@ -35,14 +38,22 @@ markautoused(Prog* p) // Fixup instructions after compactframe has moved all autos around. void -fixautoused(Prog* p) +fixautoused(Prog *p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.type == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } if (p->from.type == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.type == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } @@ -50,15 +61,18 @@ fixautoused(Prog* p) /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; Node reg, con; + Node r1; switch(proc) { default: @@ -66,14 +80,38 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ACALL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ACALL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(®, types[tptr], D_DX); + nodreg(&r1, types[tptr], D_BX); + gmove(f, ®); + reg.op = OINDREG; + gmove(®, &r1); + reg.op = OREGISTER; + gins(ACALL, ®, &r1); + break; + + case 3: // normal call of c function pointer + gins(ACALL, N, f); break; case 1: // call in new proc (go) case 2: // deferred call (defer) nodreg(®, types[TINT64], D_CX); - gins(APUSHQ, f, N); + if(flag_largemodel) { + regalloc(&r1, f->type, f); + gmove(f, &r1); + gins(APUSHQ, &r1, N); + regfree(&r1); + } else { + gins(APUSHQ, f, N); + } nodconst(&con, types[TINT32], argsize(f->type)); gins(APUSHQ, &con, N); if(proc == 1) @@ -88,7 +126,7 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTQ, ®, ®); - patch(gbranch(AJNE, T), retpc); + patch(gbranch(AJNE, T, -1), retpc); } break; } @@ -102,7 +140,7 @@ void cgen_callinter(Node *n, Node *res, int proc) { Node *i, *f; - Node tmpi, nodo, nodr, nodsp; + Node tmpi, nodi, nodo, nodr, nodsp; i = n->left; if(i->op != ODOTINTER) @@ -122,21 +160,34 @@ cgen_callinter(Node *n, Node *res, int proc) genlist(n->list); // assign the args - regalloc(&nodr, types[tptr], res); - regalloc(&nodo, types[tptr], &nodr); - nodo.op = OINDREG; - - agen(i, &nodr); // REG = &inter + // i is now addable, prepare an indirected + // register to hold its address. + igen(i, &nodi, res); // REG = &inter nodindreg(&nodsp, types[tptr], D_SP); - nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 8(REG) -- i.data - - nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab - + nodi.type = types[tptr]; + nodi.xoffset += widthptr; + cgen(&nodi, &nodsp); // 0(SP) = 8(REG) -- i.data + + regalloc(&nodo, types[tptr], res); + nodi.type = types[tptr]; + nodi.xoffset -= widthptr; + cgen(&nodi, &nodo); // REG = 0(REG) -- i.tab + regfree(&nodi); + + regalloc(&nodr, types[tptr], &nodo); + if(n->left->xoffset == BADWIDTH) + fatal("cgen_callinter: badwidth"); + nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f] + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f] + proc = 3; + } else { + // go/defer. generate go func value. + gins(ALEAQ, &nodo, &nodr); // REG = &(32+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -182,7 +233,7 @@ cgen_call(Node *n, int proc) nod.type = t; ginscall(&nod, proc); regfree(&nod); - goto ret; + return; } // call pointer @@ -192,16 +243,12 @@ cgen_call(Node *n, int proc) nod.type = t; ginscall(&nod, proc); regfree(&nod); - goto ret; + return; } // call direct n->left->method = 1; ginscall(n->left, proc); - - -ret: - ; } /* @@ -389,7 +436,9 @@ cgen_asop(Node *n) hard: n2.op = 0; n1.op = 0; - if(nr->ullman >= nl->ullman || nl->addable) { + if(nr->op == OLITERAL) { + // don't allocate a register for literals. + } else if(nr->ullman >= nl->ullman || nl->addable) { regalloc(&n2, nr->type, N); cgen(nr, &n2); nr = &n2; @@ -447,10 +496,10 @@ void dodiv(int op, Node *nl, Node *nr, Node *res) { int a, check; - Node n3, n4, n5; + Node n3, n4; Type *t, *t0; Node ax, dx, ax1, n31, oldax, olddx; - Prog *p1, *p2, *p3; + Prog *p1, *p2; // Have to be careful about handling // most negative int divided by -1 correctly. @@ -501,30 +550,22 @@ dodiv(int op, Node *nl, Node *nr, Node *res) gmove(&n31, &n3); } - p3 = P; + p2 = P; if(check) { nodconst(&n4, t, -1); gins(optoas(OCMP, t), &n3, &n4); - p1 = gbranch(optoas(ONE, t), T); - nodconst(&n4, t, -1LL<<(t->width*8-1)); - if(t->width == 8) { - n5 = n4; - regalloc(&n4, t, N); - gins(AMOVQ, &n5, &n4); - } - gins(optoas(OCMP, t), &ax, &n4); - p2 = gbranch(optoas(ONE, t), T); - if(op == ODIV) - gmove(&n4, res); - if(t->width == 8) - regfree(&n4); - if(op == OMOD) { + p1 = gbranch(optoas(ONE, t), T, +1); + if(op == ODIV) { + // a / (-1) is -a. + gins(optoas(OMINUS, t), N, &ax); + gmove(&ax, res); + } else { + // a % (-1) is 0. nodconst(&n4, t, 0); gmove(&n4, res); } - p3 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - patch(p2, pc); } savex(D_DX, &dx, &olddx, res, t); if(!issigned[t->etype]) { @@ -540,7 +581,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res) gmove(&dx, res); restx(&dx, &olddx); if(check) - patch(p3, pc); + patch(p2, pc); restx(&ax, &oldax); } @@ -594,134 +635,21 @@ restx(Node *x, Node *oldx) void cgen_div(int op, Node *nl, Node *nr, Node *res) { - Node n1, n2, n3, savl, savr; - Node ax, dx, oldax, olddx; - int n, w, s, a; + Node n1, n2, n3; + int w, a; Magic m; - if(nl->ullman >= UINF) { - tempname(&savl, nl->type); - cgen(nl, &savl); - nl = &savl; - } - if(nr->ullman >= UINF) { - tempname(&savr, nr->type); - cgen(nr, &savr); - nr = &savr; - } - if(nr->op != OLITERAL) goto longdiv; - - // special cases of mod/div - // by a constant w = nl->type->width*8; - s = 0; - n = powtwo(nr); - if(n >= 1000) { - // negative power of 2 - s = 1; - n -= 1000; - } - - if(n+1 >= w) { - // just sign bit - goto longdiv; - } - if(n < 0) - goto divbymul; - switch(n) { - case 0: - // divide by 1 - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(op == OMOD) { - gins(optoas(OXOR, nl->type), &n1, &n1); - } else - if(s) - gins(optoas(OMINUS, nl->type), N, &n1); - gmove(&n1, res); - regfree(&n1); - return; - case 1: - // divide by 2 - if(op == OMOD) { - if(issigned[nl->type->etype]) - goto longmod; - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - nodconst(&n2, nl->type, 1); - gins(optoas(OAND, nl->type), &n2, &n1); - gmove(&n1, res); - regfree(&n1); - return; - } - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(!issigned[nl->type->etype]) - break; - - // develop -1 iff nl is negative - regalloc(&n2, nl->type, N); - gmove(&n1, &n2); - nodconst(&n3, nl->type, w-1); - gins(optoas(ORSH, nl->type), &n3, &n2); - gins(optoas(OSUB, nl->type), &n2, &n1); - regfree(&n2); - break; - default: - if(op == OMOD) { - if(issigned[nl->type->etype]) - goto longmod; - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - nodconst(&n2, nl->type, mpgetfix(nr->val.u.xval)-1); - if(!smallintconst(&n2)) { - regalloc(&n3, nl->type, N); - gmove(&n2, &n3); - gins(optoas(OAND, nl->type), &n3, &n1); - regfree(&n3); - } else - gins(optoas(OAND, nl->type), &n2, &n1); - gmove(&n1, res); - regfree(&n1); - return; - } - regalloc(&n1, nl->type, res); - cgen(nl, &n1); - if(!issigned[nl->type->etype]) - break; - - // develop (2^k)-1 iff nl is negative - regalloc(&n2, nl->type, N); - gmove(&n1, &n2); - nodconst(&n3, nl->type, w-1); - gins(optoas(ORSH, nl->type), &n3, &n2); - nodconst(&n3, nl->type, w-n); - gins(optoas(ORSH, tounsigned(nl->type)), &n3, &n2); - gins(optoas(OADD, nl->type), &n2, &n1); - regfree(&n2); - break; - } - nodconst(&n2, nl->type, n); - gins(optoas(ORSH, nl->type), &n2, &n1); - if(s) - gins(optoas(OMINUS, nl->type), N, &n1); - gmove(&n1, res); - regfree(&n1); - return; - -divbymul: + // Front end handled 32-bit division. We only need to handle 64-bit. // try to do division by multiply by (2^w)/d // see hacker's delight chapter 10 switch(simtype[nl->type->etype]) { default: goto longdiv; - case TUINT8: - case TUINT16: - case TUINT32: case TUINT64: m.w = w; m.ud = mpgetfix(nr->val.u.xval); @@ -731,47 +659,28 @@ divbymul: if(op == OMOD) goto longmod; - regalloc(&n1, nl->type, N); - cgen(nl, &n1); // num -> reg(n1) - - savex(D_AX, &ax, &oldax, res, nl->type); - savex(D_DX, &dx, &olddx, res, nl->type); - + cgenr(nl, &n1, N); nodconst(&n2, nl->type, m.um); - gmove(&n2, &ax); // const->ax - - gins(optoas(OHMUL, nl->type), &n1, N); // imul reg - if(w == 8) { - // fix up 8-bit multiply - Node ah, dl; - nodreg(&ah, types[TUINT8], D_AH); - nodreg(&dl, types[TUINT8], D_DL); - gins(AMOVB, &ah, &dl); - } + regalloc(&n3, nl->type, res); + cgen_hmul(&n1, &n2, &n3); if(m.ua) { // need to add numerator accounting for overflow - gins(optoas(OADD, nl->type), &n1, &dx); + gins(optoas(OADD, nl->type), &n1, &n3); nodconst(&n2, nl->type, 1); - gins(optoas(ORRC, nl->type), &n2, &dx); + gins(optoas(ORROTC, nl->type), &n2, &n3); nodconst(&n2, nl->type, m.s-1); - gins(optoas(ORSH, nl->type), &n2, &dx); + gins(optoas(ORSH, nl->type), &n2, &n3); } else { nodconst(&n2, nl->type, m.s); - gins(optoas(ORSH, nl->type), &n2, &dx); // shift dx + gins(optoas(ORSH, nl->type), &n2, &n3); // shift dx } - + gmove(&n3, res); regfree(&n1); - gmove(&dx, res); - - restx(&ax, &oldax); - restx(&dx, &olddx); + regfree(&n3); return; - case TINT8: - case TINT16: - case TINT32: case TINT64: m.w = w; m.sd = mpgetfix(nr->val.u.xval); @@ -781,47 +690,32 @@ divbymul: if(op == OMOD) goto longmod; - regalloc(&n1, nl->type, N); - cgen(nl, &n1); // num -> reg(n1) - - savex(D_AX, &ax, &oldax, res, nl->type); - savex(D_DX, &dx, &olddx, res, nl->type); - + cgenr(nl, &n1, res); nodconst(&n2, nl->type, m.sm); - gmove(&n2, &ax); // const->ax - - gins(optoas(OHMUL, nl->type), &n1, N); // imul reg - if(w == 8) { - // fix up 8-bit multiply - Node ah, dl; - nodreg(&ah, types[TUINT8], D_AH); - nodreg(&dl, types[TUINT8], D_DL); - gins(AMOVB, &ah, &dl); - } + regalloc(&n3, nl->type, N); + cgen_hmul(&n1, &n2, &n3); if(m.sm < 0) { // need to add numerator - gins(optoas(OADD, nl->type), &n1, &dx); + gins(optoas(OADD, nl->type), &n1, &n3); } nodconst(&n2, nl->type, m.s); - gins(optoas(ORSH, nl->type), &n2, &dx); // shift dx + gins(optoas(ORSH, nl->type), &n2, &n3); // shift n3 nodconst(&n2, nl->type, w-1); gins(optoas(ORSH, nl->type), &n2, &n1); // -1 iff num is neg - gins(optoas(OSUB, nl->type), &n1, &dx); // added + gins(optoas(OSUB, nl->type), &n1, &n3); // added if(m.sd < 0) { // this could probably be removed // by factoring it into the multiplier - gins(optoas(OMINUS, nl->type), N, &dx); + gins(optoas(OMINUS, nl->type), N, &n3); } + gmove(&n3, res); regfree(&n1); - gmove(&dx, res); - - restx(&ax, &oldax); - restx(&dx, &olddx); + regfree(&n3); return; } goto longdiv; @@ -858,12 +752,48 @@ longmod: } /* + * generate high multiply: + * res = (nl*nr) >> width + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) +{ + Type *t; + int a; + Node n1, n2, ax, dx, *tmp; + + t = nl->type; + a = optoas(OHMUL, t); + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + cgenr(nl, &n1, res); + cgenr(nr, &n2, N); + nodreg(&ax, t, D_AX); + gmove(&n1, &ax); + gins(a, &n2, N); + regfree(&n2); + regfree(&n1); + + if(t->width == 1) { + // byte multiply behaves differently. + nodreg(&ax, t, D_AH); + nodreg(&dx, t, D_DL); + gmove(&ax, &dx); + } + nodreg(&dx, t, D_DX); + gmove(&dx, res); +} + +/* * generate shift according to op, one of: * res = nl << nr * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, n3, n4, n5, cx, oldcx; int a, rcx; @@ -878,7 +808,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) cgen(nl, &n1); sc = mpgetfix(nr->val.u.xval); if(sc >= nl->type->width*8) { - // large shift gets 2 shifts by width + // large shift gets 2 shifts by width-1 nodconst(&n3, types[TUINT32], nl->type->width*8-1); gins(a, &n3, &n1); gins(a, &n3, &n1); @@ -937,17 +867,20 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) regfree(&n3); // test and fix up large shifts - nodconst(&n3, tcount, nl->type->width*8); - gins(optoas(OCMP, tcount), &n1, &n3); - p1 = gbranch(optoas(OLT, tcount), T); - if(op == ORSH && issigned[nl->type->etype]) { - nodconst(&n3, types[TUINT32], nl->type->width*8-1); - gins(a, &n3, &n2); - } else { - nodconst(&n3, nl->type, 0); - gmove(&n3, &n2); + if(!bounded) { + nodconst(&n3, tcount, nl->type->width*8); + gins(optoas(OCMP, tcount), &n1, &n3); + p1 = gbranch(optoas(OLT, tcount), T, +1); + if(op == ORSH && issigned[nl->type->etype]) { + nodconst(&n3, types[TUINT32], nl->type->width*8-1); + gins(a, &n3, &n2); + } else { + nodconst(&n3, nl->type, 0); + gmove(&n3, &n2); + } + patch(p1, pc); } - patch(p1, pc); + gins(a, &n1, &n2); if(oldcx.op != 0) { @@ -968,46 +901,40 @@ ret: /* * generate byte multiply: * res = nl * nr - * no 2-operand byte multiply instruction so have to do - * 16-bit multiply and take bottom half. + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. */ void cgen_bmul(int op, Node *nl, Node *nr, Node *res) { - Node n1b, n2b, n1w, n2w; + Node n1, n2, n1b, n2b, *tmp; Type *t; int a; - if(nl->ullman >= nr->ullman) { - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - } else { - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); + // largest ullman on left. + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; } - // copy from byte to short registers - t = types[TUINT16]; - if(issigned[nl->type->etype]) - t = types[TINT16]; - - regalloc(&n2w, t, &n2b); - cgen(&n2b, &n2w); - - regalloc(&n1w, t, &n1b); - cgen(&n1b, &n1w); + // generate operands in "8-bit" registers. + regalloc(&n1b, nl->type, res); + cgen(nl, &n1b); + regalloc(&n2b, nr->type, N); + cgen(nr, &n2b); + // perform full-width multiplication. + t = types[TUINT64]; + if(issigned[nl->type->etype]) + t = types[TINT64]; + nodreg(&n1, t, n1b.val.u.reg); + nodreg(&n2, t, n2b.val.u.reg); a = optoas(op, t); - gins(a, &n2w, &n1w); - cgen(&n1w, &n1b); - cgen(&n1b, res); + gins(a, &n2, &n1); - regfree(&n1w); - regfree(&n2w); + // truncate. + gmove(&n1, res); regfree(&n1b); regfree(&n2b); } @@ -1024,9 +951,9 @@ clearfat(Node *nl) w = nl->type->width; - if(w == 16) - if(componentgen(N, nl)) - return; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; c = w % 8; // bytes q = w / 8; // quads @@ -1060,366 +987,3 @@ clearfat(Node *nl) restx(&n1, &oldn1); restx(&ax, &oldax); } - -static int -regcmp(const void *va, const void *vb) -{ - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} - -static Prog* throwpc; - -void -getargs(NodeList *nn, Node *reg, int n) -{ - NodeList *l; - int i; - - throwpc = nil; - - l = nn; - for(i=0; i<n; i++) { - if(!smallintconst(l->n->right) && !isslice(l->n->right->type)) { - regalloc(reg+i, l->n->right->type, N); - cgen(l->n->right, reg+i); - } else - reg[i] = *l->n->right; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; - } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; i<n; i++) - reg[i].local = 0; -} - -void -cmpandthrow(Node *nl, Node *nr) -{ - vlong cl; - Prog *p1; - int op; - Node *c; - Type *t; - Node n1; - - if(nl->op == OCONV && is64(nl->type)) - nl = nl->left; - if(nr->op == OCONV && is64(nr->type)) - nr = nr->left; - - op = OLE; - if(smallintconst(nl)) { - cl = mpgetfix(nl->val.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - if(is64(nr->type) && smallintconst(nr)) - nr->type = types[TUINT32]; - - n1.op = OXXX; - t = types[TUINT32]; - if(nl->type->width != t->width || nr->type->width != t->width) { - if((is64(nl->type) && nl->op != OLITERAL) || (is64(nr->type) && nr->op != OLITERAL)) - t = types[TUINT64]; - - // Check if we need to use a temporary. - // At least one of the arguments is 32 bits - // (the len or cap) so one temporary suffices. - if(nl->type->width != t->width && nl->op != OLITERAL) { - regalloc(&n1, t, nl); - gmove(nl, &n1); - nl = &n1; - } else if(nr->type->width != t->width && nr->op != OLITERAL) { - regalloc(&n1, t, nr); - gmove(nr, &n1); - nr = &n1; - } - } - gins(optoas(OCMP, t), nl, nr); - if(n1.op != OXXX) - regfree(&n1); - if(throwpc == nil) { - p1 = gbranch(optoas(op, t), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); - } else { - op = brcom(op); - p1 = gbranch(optoas(op, t), T); - patch(p1, throwpc); - } -} - -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; -} - -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) -{ - Node nodes[5]; - Node n1, n2, nres, ntemp; - vlong v; - int i, narg, nochk; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &n2); - } - - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) - ginscon(optoas(OADD, types[tptr]), v, &nodes[0]); - } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[4], &n1); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - regfree(&n1); - } - gins(optoas(OAS, types[tptr]), &nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; - -sliceslice: - nochk = n->etype; // skip bounds checking - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); - - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; - } - - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(!nochk) - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], N); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - if(!nochk) { - // if(hb[2] > old.cap[0]) goto throw; - cmpandthrow(&nodes[2], &n2); - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - } - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } - } - - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.xoffset += Array_array; - n2.type = types[tptr]; - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gins(optoas(OAS, types[tptr]), &n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) - ginscon(optoas(OADD, types[tptr]), v, &n1); - } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[3], &n1); - gins(optoas(OADD, types[tptr]), &n2, &n1); - } - - n2 = nres; - n2.xoffset += Array_array; - n2.type = types[tptr]; - gins(optoas(OAS, types[tptr]), &n1, &n2); - regfree(&n1); - - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - - if(!sleasy(res)) { - cgen(&nres, res); - } - return 1; - -no: - return 0; -} diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index 8c9208374..508a3548f 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -94,9 +94,9 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) switch(a->type) { case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; default: t |= T_TYPE; @@ -139,7 +139,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) if(t & T_SYM) /* implies sym */ Bputc(b, s); if(t & T_FCONST) { - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -153,7 +153,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) return; } if(t & T_SCONST) { - n = a->sval; + n = a->u.sval; for(i=0; i<NSNAME; i++) { Bputc(b, *n); n++; @@ -295,7 +295,7 @@ dsname(Sym *s, int off, char *t, int n) p->to.type = D_SCONST; p->to.index = D_NONE; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -312,8 +312,8 @@ datastring(char *s, int len, Addr *a) a->type = D_EXTERN; a->sym = sym; a->node = sym->def; - a->offset = widthptr+4; // skip header - a->etype = TINT32; + a->offset = widthptr+widthint; // skip header + a->etype = simtype[TINT]; } /* @@ -324,7 +324,7 @@ void datagostring(Strlit *sval, Addr *a) { Sym *sym; - + sym = stringsym(sval->s, sval->len); a->type = D_EXTERN; a->sym = sym; @@ -364,13 +364,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->from.scale = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->from.scale = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void @@ -386,10 +386,10 @@ gdatastring(Node *nam, Strlit *sval) p->to.type = D_ADDR; //print("%P\n", p); - nodconst(&nod1, types[TINT32], sval->len); + nodconst(&nod1, types[TINT], sval->len); p = gins(ADATA, nam, &nod1); - p->from.scale = types[TINT32]->width; - p->from.offset += types[tptr]->width; + p->from.scale = widthint; + p->from.offset += widthptr; } int @@ -408,7 +408,7 @@ dstringptr(Sym *s, int off, char *str) datastring(str, strlen(str)+1, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; - p->to.etype = TINT32; + p->to.etype = simtype[TINT]; off += widthptr; return off; @@ -432,7 +432,7 @@ dgostrlitptr(Sym *s, int off, Strlit *lit) datagostring(lit, &p->to); p->to.index = p->to.type; p->to.type = D_ADDR; - p->to.etype = TINT32; + p->to.etype = simtype[TINT]; off += widthptr; return off; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index ededcf673..fc5407a1f 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -103,9 +103,13 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; @@ -113,7 +117,11 @@ gbranch(int as, Type *t) p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; + if(as != AJMP && likely != 0) { + p->from.type = D_CONST; + p->from.offset = likely > 0; + } return p; } @@ -125,7 +133,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -136,8 +144,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -165,44 +173,6 @@ newplist(void) } void -clearstk(void) -{ - Plist *pl; - Prog *p1, *p2; - Node sp, di, cx, con, ax; - - if((uint32)plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - nodreg(&sp, types[tptr], D_SP); - nodreg(&di, types[tptr], D_DI); - nodreg(&cx, types[TUINT64], D_CX); - nodconst(&con, types[TUINT64], (uint32)p1->to.offset / widthptr); - gins(ACLD, N, N); - gins(AMOVQ, &sp, &di); - gins(AMOVQ, &con, &cx); - nodconst(&con, types[TUINT64], 0); - nodreg(&ax, types[TUINT64], D_AX); - gins(AMOVQ, &con, &ax); - gins(AREP, N, N); - gins(ASTOSQ, N, N); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - -void gused(Node *n) { gins(ANOP, n, N); // used @@ -213,22 +183,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AJMP, T); + p = gbranch(AJMP, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->from.scale = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -236,7 +207,18 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.sym = s; +} + +void +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -248,8 +230,9 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.index = D_NONE; p->to.offset = width; if(dupok) - p->from.scale = DUPOK; - p->from.scale |= RODATA; + p->from.scale |= DUPOK; + if(rodata) + p->from.scale |= RODATA; } int @@ -272,11 +255,12 @@ isfat(Type *t) * call afunclit to fix up the argument. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_ADDR && a->index == D_EXTERN) { a->type = D_EXTERN; a->index = D_NONE; + a->sym = n->sym; } } @@ -300,7 +284,7 @@ ginit(void) reg[i] = 1; for(i=D_AX; i<=D_R15; i++) reg[i] = 0; - for(i=D_X0; i<=D_X7; i++) + for(i=D_X0; i<=D_X15; i++) reg[i] = 0; for(i=0; i<nelem(resvd); i++) @@ -318,7 +302,7 @@ gclean(void) for(i=D_AX; i<=D_R15; i++) if(reg[i]) yyerror("reg %R left allocated\n", i); - for(i=D_X0; i<=D_X7; i++) + for(i=D_X0; i<=D_X15; i++) if(reg[i]) yyerror("reg %R left allocated\n", i); } @@ -388,10 +372,10 @@ regalloc(Node *n, Type *t, Node *o) case TFLOAT64: if(o != N && o->op == OREGISTER) { i = o->val.u.reg; - if(i >= D_X0 && i <= D_X7) + if(i >= D_X0 && i <= D_X15) goto out; } - for(i=D_X0; i<=D_X7; i++) + for(i=D_X0; i<=D_X15; i++) if(reg[i] == 0) goto out; fatal("out of floating registers"); @@ -572,6 +556,10 @@ ismem(Node *n) case ONAME: case OPARAM: return 1; + case OADDR: + if(flag_largemodel) + return 1; + break; } return 0; } @@ -616,7 +604,7 @@ gmove(Node *f, Node *t) Prog *p1, *p2; if(debug['M']) - print("gmove %N -> %N\n", f, t); + print("gmove %lN -> %lN\n", f, t); ft = simsimtype(f->type); tt = simsimtype(t->type); @@ -706,11 +694,14 @@ gmove(Node *f, Node *t) case CASE(TINT32, TUINT32): case CASE(TUINT32, TINT32): case CASE(TUINT32, TUINT32): + a = AMOVL; + break; + case CASE(TINT64, TINT32): // truncate case CASE(TUINT64, TINT32): case CASE(TINT64, TUINT32): case CASE(TUINT64, TUINT32): - a = AMOVL; + a = AMOVQL; break; case CASE(TINT64, TINT64): // same size @@ -822,9 +813,9 @@ gmove(Node *f, Node *t) // algorithm is: // if small enough, use native float64 -> int64 conversion. // otherwise, subtract 2^63, convert, and add it back. - a = ACVTSS2SQ; + a = ACVTTSS2SQ; if(ft == TFLOAT64) - a = ACVTSD2SQ; + a = ACVTTSD2SQ; bignodes(); regalloc(&r1, types[ft], N); regalloc(&r2, types[tt], t); @@ -832,9 +823,9 @@ gmove(Node *f, Node *t) regalloc(&r4, types[tt], N); gins(optoas(OAS, f->type), f, &r1); gins(optoas(OCMP, f->type), &bigf, &r1); - p1 = gbranch(optoas(OLE, f->type), T); + p1 = gbranch(optoas(OLE, f->type), T, +1); gins(a, &r1, &r2); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); gins(optoas(OAS, f->type), &bigf, &r3); gins(optoas(OSUB, f->type), &r3, &r1); @@ -903,9 +894,9 @@ gmove(Node *f, Node *t) regalloc(&r4, f->type, N); gmove(f, &r1); gins(ACMPQ, &r1, &zero); - p1 = gbranch(AJLT, T); + p1 = gbranch(AJLT, T, +1); gins(a, &r1, &r2); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); gmove(&r1, &r3); gins(ASHRQ, &one, &r3); @@ -1016,6 +1007,13 @@ gins(int as, Node *f, Node *t) case AMOVSD: if(f != N && t != N && samaddr(f, t)) return nil; + break; + + case ALEAQ: + if(f != N && isconst(f, CTNIL)) { + fatal("gins LEAQ nil %T", f->type); + } + break; } memset(&af, 0, sizeof af); @@ -1047,13 +1045,34 @@ gins(int as, Node *f, Node *t) w = 8; break; } - if(w != 0 && f != N && (af.width > w || at.width > w)) { + if(w != 0 && ((f != N && af.width < w) || (t != N && at.width > w))) { + dump("f", f); + dump("t", t); fatal("bad width: %P (%d, %d)\n", p, af.width, at.width); } return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m, types[TUINTPTR], n); + cgen(n, &m); + m.xoffset = 0; + m.op = OINDREG; + m.type = types[TUINT8]; + gins(ATESTB, nodintconst(0), &m); + regfree(&m); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1079,14 +1098,22 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->scale = 0; a->index = D_NONE; a->type = D_NONE; a->gotype = S; a->node = N; + a->width = 0; if(n == N) return; + if(n->type != T && n->type->etype != TIDEAL) { + dowidth(n->type); + a->width = n->type->width; + } + switch(n->op) { default: fatal("naddr: bad %O %D", n->op, a); @@ -1134,15 +1161,27 @@ naddr(Node *n, Addr *a, int canemitcode) a->type = D_PARAM; a->node = n->left->orig; break; + + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVQ, N, N); + p->from.type = D_DX+D_INDIR; + p->from.offset = n->xoffset; + p->to.type = D_BX; + a->type = D_BX; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; case ONAME: a->etype = 0; - a->width = 0; - if(n->type != T) { + if(n->type != T) a->etype = simtype[n->type->etype]; - a->width = n->type->width; - a->gotype = ngotype(n); - } a->offset = n->xoffset; a->sym = n->sym; a->node = n->orig; @@ -1173,6 +1212,8 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->index = D_EXTERN; a->type = D_ADDR; + a->width = widthptr; + a->sym = funcsym(a->sym); break; } break; @@ -1184,7 +1225,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: @@ -1210,6 +1251,7 @@ naddr(Node *n, Addr *a, int canemitcode) case OADDR: naddr(n->left, a, canemitcode); + a->width = widthptr; if(a->type >= D_INDIR) { a->type -= D_INDIR; break; @@ -1239,9 +1281,9 @@ naddr(Node *n, Addr *a, int canemitcode) naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // len(nil) - a->etype = TUINT32; + a->etype = simtype[TUINT]; a->offset += Array_nel; - a->width = 4; + a->width = widthint; if(a->offset >= unmappedzero && a->offset-Array_nel < unmappedzero) checkoffset(a, canemitcode); break; @@ -1251,9 +1293,9 @@ naddr(Node *n, Addr *a, int canemitcode) naddr(n->left, a, canemitcode); if(a->type == D_CONST && a->offset == 0) break; // cap(nil) - a->etype = TUINT32; + a->etype = simtype[TUINT]; a->offset += Array_cap; - a->width = 4; + a->width = widthint; if(a->offset >= unmappedzero && a->offset-Array_cap < unmappedzero) checkoffset(a, canemitcode); break; @@ -1645,6 +1687,28 @@ optoas(int op, Type *t) a = AXORQ; break; + case CASE(OLROT, TINT8): + case CASE(OLROT, TUINT8): + a = AROLB; + break; + + case CASE(OLROT, TINT16): + case CASE(OLROT, TUINT16): + a = AROLW; + break; + + case CASE(OLROT, TINT32): + case CASE(OLROT, TUINT32): + case CASE(OLROT, TPTR32): + a = AROLL; + break; + + case CASE(OLROT, TINT64): + case CASE(OLROT, TUINT64): + case CASE(OLROT, TPTR64): + a = AROLQ; + break; + case CASE(OLSH, TINT8): case CASE(OLSH, TUINT8): a = ASHLB; @@ -1701,23 +1765,23 @@ optoas(int op, Type *t) a = ASARQ; break; - case CASE(ORRC, TINT8): - case CASE(ORRC, TUINT8): + case CASE(ORROTC, TINT8): + case CASE(ORROTC, TUINT8): a = ARCRB; break; - case CASE(ORRC, TINT16): - case CASE(ORRC, TUINT16): + case CASE(ORROTC, TINT16): + case CASE(ORROTC, TUINT16): a = ARCRW; break; - case CASE(ORRC, TINT32): - case CASE(ORRC, TUINT32): + case CASE(ORROTC, TINT32): + case CASE(ORROTC, TUINT32): a = ARCRL; break; - case CASE(ORRC, TINT64): - case CASE(ORRC, TUINT64): + case CASE(ORROTC, TINT64): + case CASE(ORROTC, TUINT64): a = ARCRQ; break; @@ -1919,6 +1983,9 @@ sudoaddable(int as, Node *n, Addr *a) goto odot; case OINDEX: + return 0; + // disabled: OINDEX case is now covered by agenr + // for a more suitable register allocation pattern. if(n->left->type->etype == TSTRING) return 0; goto oindex; @@ -2053,32 +2120,20 @@ oindex: } // check bounds - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds n4.op = OXXX; - t = types[TUINT32]; + t = types[simtype[TUINT]]; if(o & ODynam) { if(o & OAddable) { n2 = *l; n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(is64(r->type)) { - t = types[TUINT64]; - regalloc(&n4, t, N); - gmove(&n2, &n4); - n2 = n4; - } + n2.type = types[simtype[TUINT]]; } else { n2 = *reg; n2.xoffset = Array_nel; n2.op = OINDREG; - n2.type = types[TUINT32]; - if(is64(r->type)) { - t = types[TUINT64]; - regalloc(&n4, t, N); - gmove(&n2, &n4); - n2 = n4; - } + n2.type = types[simtype[TUINT]]; } } else { if(is64(r->type)) @@ -2086,10 +2141,10 @@ oindex: nodconst(&n2, types[TUINT64], l->type->bound); } gins(optoas(OCMP, t), reg1, &n2); - p1 = gbranch(optoas(OLT, t), T); + p1 = gbranch(optoas(OLT, t), T, +1); if(n4.op != OXXX) regfree(&n4); - ginscall(panicindex, 0); + ginscall(panicindex, -1); patch(p1, pc); } @@ -2140,19 +2195,19 @@ oindex_const: reg->op = OEMPTY; reg1->op = OEMPTY; - regalloc(reg, types[tptr], N); - agen(l, reg); - if(o & ODynam) { - if(!debug['B'] && !n->etype) { + regalloc(reg, types[tptr], N); + agen(l, reg); + + if(!debug['B'] && !n->bounded) { n1 = *reg; n1.op = OINDREG; n1.type = types[tptr]; n1.xoffset = Array_nel; nodconst(&n2, types[TUINT64], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + gins(optoas(OCMP, types[simtype[TUINT]]), &n1, &n2); + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } @@ -2162,14 +2217,24 @@ oindex_const: n1.xoffset = Array_array; gmove(&n1, reg); + n2 = *reg; + n2.op = OINDREG; + n2.xoffset = v*w; + a->type = D_NONE; + a->index = D_NONE; + naddr(&n2, a, 1); + goto yes; } - - n2 = *reg; - n2.op = OINDREG; - n2.xoffset = v*w; + + igen(l, &n1, N); + if(n1.op == OINDREG) { + *reg = n1; + reg->op = OREGISTER; + } + n1.xoffset += v*w; a->type = D_NONE; - a->index = D_NONE; - naddr(&n2, a, 1); + a->index= D_NONE; + naddr(&n1, a, 1); goto yes; oindex_const_sudo: @@ -2180,13 +2245,13 @@ oindex_const_sudo: } // slice indexed by a constant - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { a->offset += Array_nel; nodconst(&n2, types[TUINT64], v); - p1 = gins(optoas(OCMP, types[TUINT32]), N, &n2); + p1 = gins(optoas(OCMP, types[simtype[TUINT]]), N, &n2); p1->from = *a; - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + p1 = gbranch(optoas(OGT, types[simtype[TUINT]]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); a->offset -= Array_nel; } diff --git a/src/cmd/6g/list.c b/src/cmd/6g/list.c index ad63f7d29..9d27a6a09 100644 --- a/src/cmd/6g/list.c +++ b/src/cmd/6g/list.c @@ -107,10 +107,10 @@ Dconv(Fmt *fp) break; case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) snprint(str, sizeof(str), "<nil>"); else - snprint(str, sizeof(str), "%d", a->branch->loc); + snprint(str, sizeof(str), "%d", a->u.branch->loc); break; case D_EXTERN: @@ -140,11 +140,11 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; case D_ADDR: @@ -161,7 +161,10 @@ brk: strcat(str, s); } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } static char* regstr[] = diff --git a/src/cmd/6g/opt.h b/src/cmd/6g/opt.h index 9a8866b8d..9b0ea1b5a 100644 --- a/src/cmd/6g/opt.h +++ b/src/cmd/6g/opt.h @@ -34,8 +34,6 @@ #define D_HI D_NONE #define D_LO D_NONE -#define isregtype(t) ((t)>= D_AX && (t)<=D_R15) - #define BLOAD(r) band(bnot(r->refbehind), r->refahead) #define BSTORE(r) band(bnot(r->calbehind), r->calahead) #define LOAD(r) (~r->refbehind.b[z] & r->refahead.b[z]) @@ -49,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -70,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index 3710033b2..569655786 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -34,6 +34,10 @@ #include "opt.h" static void conprop(Reg *r); +static void elimshortmov(Reg *r); +static int prevl(Reg *r, int reg); +static void pushback(Reg *r); +static int regconsttyp(Adr*); // do we need the carry bit static int @@ -45,11 +49,17 @@ needc(Prog *p) case AADCQ: case ASBBL: case ASBBQ: + case ARCRB: + case ARCRW: case ARCRL: case ARCRQ: return 1; + case AADDB: + case AADDW: case AADDL: case AADDQ: + case ASUBB: + case ASUBW: case ASUBL: case ASUBQ: case AJMP: @@ -122,9 +132,14 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } + + // byte, word arithmetic elimination. + elimshortmov(r); // constant propagation // find MOV $con,R followed by @@ -200,6 +215,7 @@ loop1: case AMOVWQZX: case AMOVLQSX: case AMOVLQZX: + case AMOVQL: if(regtyp(&p->to)) { r1 = rnops(uniqs(r)); if(r1 != R) { @@ -272,6 +288,115 @@ loop1: } if(t) goto loop1; + + // MOVLQZX removal. + // The MOVLQZX exists to avoid being confused for a + // MOVL that is just copying 32-bit data around during + // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 + // if it is dominated by an earlier ADDL/MOVL/etc into R1 that + // will have already cleared the high bits. + // + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->as == AMOVLQZX) + if(regtyp(&p->from)) + if(p->from.type == p->to.type) + if(prevl(r, p->from.type)) + excise(r); + + if(p->as == AMOVSD) + if(regtyp(&p->from)) + if(regtyp(&p->to)) + p->as = AMOVAPD; + } + + // load pipelining + // push any load from memory as early as possible + // to give it time to complete before use. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + switch(p->as) { + case AMOVB: + case AMOVW: + case AMOVL: + case AMOVQ: + case AMOVLQZX: + if(regtyp(&p->to) && !regconsttyp(&p->from)) + pushback(r); + } + } +} + +static void +pushback(Reg *r0) +{ + Reg *r, *b; + Prog *p0, *p, t; + + b = R; + p0 = r0->prog; + for(r=uniqp(r0); r!=R && uniqs(r)!=R; r=uniqp(r)) { + p = r->prog; + if(p->as != ANOP) { + if(!regconsttyp(&p->from) || !regtyp(&p->to)) + break; + if(copyu(p, &p0->to, A) || copyu(p0, &p->to, A)) + break; + } + if(p->as == ACALL) + break; + b = r; + } + + if(b == R) { + if(debug['v']) { + print("no pushback: %P\n", r0->prog); + if(r) + print("\t%P [%d]\n", r->prog, uniqs(r)!=R); + } + return; + } + + if(debug['v']) { + print("pushback\n"); + for(r=b;; r=r->link) { + print("\t%P\n", r->prog); + if(r == r0) + break; + } + } + + t = *r0->prog; + for(r=uniqp(r0);; r=uniqp(r)) { + p0 = r->link->prog; + p = r->prog; + p0->as = p->as; + p0->lineno = p->lineno; + p0->from = p->from; + p0->to = p->to; + + if(r == b) + break; + } + p0 = r->prog; + p0->as = t.as; + p0->lineno = t.lineno; + p0->from = t.from; + p0->to = t.to; + + if(debug['v']) { + print("\tafter\n"); + for(r=b;; r=r->link) { + print("\t%P\n", r->prog); + if(r == r0) + break; + } + } } void @@ -335,6 +460,155 @@ regtyp(Adr *a) return 0; } +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 32-bit register without +// causing any trouble. +static void +elimshortmov(Reg *r) +{ + Prog *p; + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(regtyp(&p->to)) { + switch(p->as) { + case AINCB: + case AINCW: + p->as = AINCQ; + break; + case ADECB: + case ADECW: + p->as = ADECQ; + break; + case ANEGB: + case ANEGW: + p->as = ANEGQ; + break; + case ANOTB: + case ANOTW: + p->as = ANOTQ; + break; + } + if(regtyp(&p->from) || p->from.type == D_CONST) { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 64-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch(p->as) { + case AMOVB: + case AMOVW: + p->as = AMOVQ; + break; + case AADDB: + case AADDW: + if(!needc(p->link)) + p->as = AADDQ; + break; + case ASUBB: + case ASUBW: + if(!needc(p->link)) + p->as = ASUBQ; + break; + case AMULB: + case AMULW: + p->as = AMULQ; + break; + case AIMULB: + case AIMULW: + p->as = AIMULQ; + break; + case AANDB: + case AANDW: + p->as = AANDQ; + break; + case AORB: + case AORW: + p->as = AORQ; + break; + case AXORB: + case AXORW: + p->as = AXORQ; + break; + case ASHLB: + case ASHLW: + p->as = ASHLQ; + break; + } + } else if(p->from.type >= D_NONE) { + // explicit zero extension, but don't + // do that if source is a byte register + // (only AH can occur and it's forbidden). + switch(p->as) { + case AMOVB: + p->as = AMOVBQZX; + break; + case AMOVW: + p->as = AMOVWQZX; + break; + } + } + } + } +} + +int +regconsttyp(Adr *a) +{ + if(regtyp(a)) + return 1; + switch(a->type) { + case D_CONST: + case D_FCONST: + case D_SCONST: + case D_ADDR: + return 1; + } + return 0; +} + +// is reg guaranteed to be truncated by a previous L instruction? +static int +prevl(Reg *r0, int reg) +{ + Prog *p; + Reg *r; + + for(r=uniqp(r0); r!=R; r=uniqp(r)) { + p = r->prog; + if(p->to.type == reg) { + switch(p->as) { + case AADDL: + case AANDL: + case ADECL: + case ADIVL: + case AIDIVL: + case AIMULL: + case AINCL: + case AMOVL: + case AMULL: + case AORL: + case ARCLL: + case ARCRL: + case AROLL: + case ARORL: + case ASALL: + case ASARL: + case ASHLL: + case ASHRL: + case ASUBL: + case AXORL: + return 1; + } + return 0; + } + } + return 0; +} + /* * the idea is to substitute * one register for another @@ -357,19 +631,34 @@ subprop(Reg *r0) Reg *r; int t; + if(debug['P'] && debug['v']) + print("subprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; - if(!regtyp(v1)) + if(!regtyp(v1)) { + if(debug['P'] && debug['v']) + print("\tnot regtype %D; return 0\n", v1); return 0; + } v2 = &p->to; - if(!regtyp(v2)) + if(!regtyp(v2)) { + if(debug['P'] && debug['v']) + print("\tnot regtype %D; return 0\n", v2); return 0; + } for(r=uniqp(r0); r!=R; r=uniqp(r)) { - if(uniqs(r) == R) + if(debug['P'] && debug['v']) + print("\t? %P\n", r->prog); + if(uniqs(r) == R) { + if(debug['P'] && debug['v']) + print("\tno unique successor\n"); break; + } p = r->prog; switch(p->as) { case ACALL: + if(debug['P'] && debug['v']) + print("\tfound %P; return 0\n", p); return 0; case AIMULL: @@ -377,20 +666,7 @@ subprop(Reg *r0) case AIMULW: if(p->to.type != D_NONE) break; - - case ADIVB: - case ADIVL: - case ADIVQ: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVQ: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULQ: - case AMULW: + goto giveup; case ARCLB: case ARCLL: @@ -424,6 +700,23 @@ subprop(Reg *r0) case ASHRL: case ASHRQ: case ASHRW: + if(p->from.type == D_CONST) + break; + goto giveup; + + case ADIVB: + case ADIVL: + case ADIVQ: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVQ: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULQ: + case AMULW: case AREP: case AREPN: @@ -438,21 +731,34 @@ subprop(Reg *r0) case AMOVSB: case AMOVSL: case AMOVSQ: + giveup: + if(debug['P'] && debug['v']) + print("\tfound %P; return 0\n", p); return 0; case AMOVL: case AMOVQ: + case AMOVSS: + case AMOVSD: if(p->to.type == v1->type) goto gotit; break; } if(copyau(&p->from, v2) || - copyau(&p->to, v2)) + copyau(&p->to, v2)) { + if(debug['P'] && debug['v']) + print("\tcopyau %D failed\n", v2); break; + } if(copysub(&p->from, v1, v2, 0) || - copysub(&p->to, v1, v2, 0)) + copysub(&p->to, v1, v2, 0)) { + if(debug['P'] && debug['v']) + print("\tcopysub failed\n"); break; + } } + if(debug['P'] && debug['v']) + print("\tran off end; return 0\n", p); return 0; gotit: @@ -497,6 +803,8 @@ copyprop(Reg *r0) Adr *v1, *v2; Reg *r; + if(debug['P'] && debug['v']) + print("copyprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; v2 = &p->to; @@ -636,6 +944,7 @@ copyu(Prog *p, Adr *v, Adr *s) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case AMOVSS: case AMOVSD: @@ -853,8 +1162,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == REGRET || v->type == FREGRET) - return 2; if(s != A) return 1; return 3; @@ -864,6 +1171,8 @@ copyu(Prog *p, Adr *v, Adr *s) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; + if(v->type == p->from.type) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -907,13 +1216,22 @@ int copyau(Adr *a, Adr *v) { - if(copyas(a, v)) + if(copyas(a, v)) { + if(debug['P'] && debug['v']) + print("\tcopyau: copyas returned 1\n"); return 1; + } if(regtyp(v)) { - if(a->type-D_INDIR == v->type) + if(a->type-D_INDIR == v->type) { + if(debug['P'] && debug['v']) + print("\tcopyau: found indir use - return 1\n"); return 1; - if(a->index == v->type) + } + if(a->index == v->type) { + if(debug['P'] && debug['v']) + print("\tcopyau: found index use - return 1\n"); return 1; + } } return 0; } @@ -990,7 +1308,7 @@ loop: if(p->from.node == p0->from.node) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) - if(p->from.dval == p0->from.dval) + if(p->from.u.vval == p0->from.u.vval) if(p->from.index == p0->from.index) { excise(r); goto loop; diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 049c63f17..c56d71678 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -151,6 +151,8 @@ static char* regname[] = { ".X15", }; +static Node* regnodes[NREGVAR]; + static void fixjmp(Prog*); void @@ -164,7 +166,7 @@ regopt(Prog *firstp) if(first) { fmtinstall('Q', Qconv); - exregoffset = D_R13; // R14,R15 are external + exregoffset = D_R15; first = 0; } @@ -191,8 +193,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) - var[i].node = newname(lookup(regname[i])); + for(i=0; i<NREGVAR; i++) { + if(regnodes[i] == N) + regnodes[i] = newname(lookup(regname[i])); + var[i].node = regnodes[i]; + } regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { @@ -219,6 +224,8 @@ regopt(Prog *firstp) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: continue; } r = rega(); @@ -247,6 +254,20 @@ regopt(Prog *firstp) } } + // Avoid making variables for direct-called functions. + if(p->as == ACALL && p->to.type == D_EXTERN) + continue; + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + r->use1.b[0] |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + r->use1.b[0] |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + r->use2.b[0] |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + r->use2.b[0] |= RtoB(p->to.index); + bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -326,6 +347,7 @@ regopt(Prog *firstp) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case AMOVQL: case APOPQ: case AMOVSS: @@ -578,8 +600,9 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } if(debug['R'] && debug['v']) @@ -593,9 +616,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->reg; + r1 = p->to.u.branch->reg; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -742,6 +765,9 @@ loop2: brk: qsort(region, nregion, sizeof(region[0]), rcmp); + if(debug['R'] && debug['v']) + dumpit("pass5", firstr); + /* * pass 6 * determine used registers (paint2) @@ -752,8 +778,16 @@ brk: bit = blsh(rgp->varno); vreg = paint2(rgp->enter, rgp->varno); vreg = allreg(vreg, rgp); - if(rgp->regno != 0) + if(rgp->regno != 0) { + if(debug['R'] && debug['v']) { + Var *v; + + v = var + rgp->varno; + print("registerize %N+%d (bit=%2d et=%2E) in %R\n", + v->node, v->offset, rgp->varno, v->etype, rgp->regno); + } paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + } rgp++; } @@ -776,8 +810,8 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; } if(lastr != R) { @@ -838,7 +872,6 @@ addmove(Reg *r, int bn, int rn, int f) a->offset = v->offset; a->etype = v->etype; a->type = v->name; - a->gotype = v->gotype; a->node = v->node; a->sym = v->node->sym; @@ -847,7 +880,7 @@ addmove(Reg *r, int bn, int rn, int f) p1->as = AMOVL; switch(v->etype) { default: - fatal("unknown type\n"); + fatal("unknown type %E", v->etype); case TINT8: case TUINT8: case TBOOL: @@ -932,7 +965,8 @@ Bits mkvar(Reg *r, Adr *a) { Var *v; - int i, t, n, et, z, w, flag; + int i, t, n, et, z, flag; + int64 w; uint32 regu; int32 o; Bits bit; @@ -984,6 +1018,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %lld for %D", w, a); flag = 0; for(i=0; i<nvar; i++) { @@ -1019,14 +1055,14 @@ mkvar(Reg *r, Adr *a) v = var+i; v->offset = o; v->name = n; - v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d %#N %D\n", i, et, w, node, a); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + ostats.nvar++; bit = blsh(i); @@ -1089,8 +1125,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; z<BITS; z++) + for(z=0; z<BITS; z++) { cal.b[z] |= externs.b[z]; + // issue 4066: flush modified return variables in case of panic + if(hasdefer) + cal.b[z] |= ovar.b[z]; + } break; } for(z=0; z<BITS; z++) { @@ -1576,7 +1616,7 @@ RtoB(int r) int BtoR(int32 b) { - b &= 0x3fffL; // no R14 or R15 + b &= 0xffffL; if(b == 0) return 0; return bitno(b) + D_AX; @@ -1584,26 +1624,26 @@ BtoR(int32 b) /* * bit reg - * 16 X5 (FREGMIN) + * 16 X0 * ... - * 26 X15 (FREGEXT) + * 31 X15 */ int32 FtoB(int f) { - if(f < FREGMIN || f > FREGEXT) + if(f < D_X0 || f > D_X15) return 0; - return 1L << (f - FREGMIN + 16); + return 1L << (f - D_X0 + 16); } int BtoF(int32 b) { - b &= 0xFF0000L; + b &= 0xFFFF0000L; if(b == 0) return 0; - return bitno(b) - 16 + FREGMIN; + return bitno(b) - 16 + D_X0; } void @@ -1719,7 +1759,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1741,9 +1781,9 @@ mark(Prog *firstp) if(p->reg != dead) break; p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); - if(p->as == AJMP || p->as == ARET || (p->as == ACALL && noreturn(p))) + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) break; } } @@ -1762,8 +1802,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AJMP) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1799,7 +1839,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 849915954..805b3fc6f 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -736,6 +736,30 @@ enum as ACRC32B, ACRC32Q, AIMUL3Q, + + APREFETCHT0, + APREFETCHT1, + APREFETCHT2, + APREFETCHNTA, + + AMOVQL, + ABSWAPL, + ABSWAPQ, + + AUNDEF, + + AAESENC, + AAESENCLAST, + AAESDEC, + AAESDECLAST, + AAESIMC, + AAESKEYGENASSIST, + + APSHUFD, + + AUSEFIELD, + ALOCALS, + ATYPE, ALAST }; @@ -794,6 +818,14 @@ enum D_X5, D_X6, D_X7, + D_X8, + D_X9, + D_X10, + D_X11, + D_X12, + D_X13, + D_X14, + D_X15, D_CS = 68, D_SS, @@ -854,7 +886,7 @@ enum /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index ee31a05cd..5fb75ba4d 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -74,34 +74,6 @@ datoff(vlong addr) return 0; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRela, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrShstrtab, - ElfStrSymtab, - ElfStrStrtab, - ElfStrRelaPlt, - ElfStrPlt, - ElfStrGnuVersion, - ElfStrGnuVersionR, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -127,6 +99,20 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); +Sym * +lookuprel(void) +{ + return lookup(".rela", 0); +} + +void +adddynrela(Sym *rela, Sym *s, Reloc *r) +{ + addaddrplus(rela, s, r->off); + adduint64(rela, R_X86_64_RELATIVE); + addaddrplus(rela, r->sym, r->add); // Addend +} + void adddynrel(Sym *s, Reloc *r) { @@ -299,6 +285,37 @@ adddynrel(Sym *s, Reloc *r) } int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + VPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + VPUT(R_X86_64_32 | (uint64)elfsym<<32); + else if(r->siz == 8) + VPUT(R_X86_64_64 | (uint64)elfsym<<32); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + else + return -1; + add -= r->siz; + break; + } + + VPUT(add); + return 0; +} + +int archreloc(Reloc *r, Sym *s, vlong *val) { USED(r); @@ -307,7 +324,7 @@ archreloc(Reloc *r, Sym *s, vlong *val) return -1; } -static void +void elfsetupplt(void) { Sym *plt, *got; @@ -434,6 +451,7 @@ adddynsym(Sym *s) Sym *d, *str; int t; char *name; + vlong off; if(s->dynid >= 0) return; @@ -452,7 +470,7 @@ adddynsym(Sym *s) adduint32(d, addstring(lookup(".dynstr", 0), name)); /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) + if(s->dynexport && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -490,7 +508,7 @@ adddynsym(Sym *s) addaddr(d, s); /* size of object */ - adduint64(d, 0); + adduint64(d, s->size); if(!s->dynexport && s->dynimplib && needlib(s->dynimplib)) { elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, @@ -502,35 +520,51 @@ adddynsym(Sym *s) name = s->dynimpname; if(name == nil) name = s->name; - s->dynid = d->size/16; + if(d->size == 0 && ndynexp > 0) { // pre-allocate for dynexps + symgrow(d, ndynexp*16); + } + if(s->dynid <= -100) { // pre-allocated, see cmd/ld/go.c:^sortdynexp() + s->dynid = -s->dynid-100; + off = s->dynid*16; + } else { + off = d->size; + s->dynid = off/16; + } // darwin still puts _ prefixes on all C symbols str = lookup(".dynstr", 0); - adduint32(d, str->size); + setuint32(d, off, str->size); + off += 4; adduint8(str, '_'); addstring(str, name); if(s->type == SDYNIMPORT) { - adduint8(d, 0x01); // type - N_EXT - external symbol - adduint8(d, 0); // section + setuint8(d, off, 0x01); // type - N_EXT - external symbol + off++; + setuint8(d, off, 0); // section + off++; } else { - adduint8(d, 0x0f); + setuint8(d, off, 0x0f); + off++; switch(s->type) { default: case STEXT: - adduint8(d, 1); + setuint8(d, off, 1); break; case SDATA: - adduint8(d, 2); + setuint8(d, off, 2); break; case SBSS: - adduint8(d, 4); + setuint8(d, off, 4); break; } + off++; } - adduint16(d, 0); // desc + setuint16(d, off, 0); // desc + off += 2; if(s->type == SDYNIMPORT) - adduint64(d, 0); // value + setuint64(d, off, 0); // value else - addaddr(d, s); + setaddr(d, off, s); + off += 8; } else if(HEADTYPE != Hwindows) { diag("adddynsym: unsupported binary format"); } @@ -557,181 +591,30 @@ adddynlib(char *lib) } void -doelf(void) -{ - Sym *s, *shstrtab, *dynstr; - - if(HEADTYPE != Hlinux && HEADTYPE != Hfreebsd && HEADTYPE != Hopenbsd && HEADTYPE != Hnetbsd) - return; - - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".elfdata"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - dwarfaddshstrings(shstrtab); - } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRela] = addstring(shstrtab, ".rela"); - elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version"); - elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->size += ELF64SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->type = SELFROSECT; - s->reachable = 1; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - - /* relocation table */ - s = lookup(".rela", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - s = lookup(".rela.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version_r", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); - elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); - elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_RELA); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); - - elfwritedynent(s, DT_DEBUG, 0); - - // Do not write DT_NULL. elfdynhash will finish it. - } -} - -void -shsym(ElfShdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(ElfPhdr *ph, ElfShdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - -void asmb(void) { int32 magic; - int a, dynsym; - vlong vl, startva, symo, dwarfoff, machlink, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + int i; + vlong vl, symo, dwarfoff, machlink; Section *sect; - int o; + Sym *sym; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elftextsh = 0; - if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -763,6 +646,7 @@ asmb(void) default: diag("unknown header type %d", HEADTYPE); case Hplan9x32: + case Hplan9x64: case Helf: break; case Hdarwin: @@ -773,16 +657,6 @@ asmb(void) case Hnetbsd: case Hopenbsd: debug['8'] = 1; /* 64-bit addresses */ - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; break; case Hwindows: break; @@ -798,7 +672,7 @@ asmb(void) Bflush(&bso); switch(HEADTYPE) { default: - case Hplan9x32: + case Hplan9x64: case Helf: debug['s'] = 1; symo = HEADR+segtext.len+segdata.filelen; @@ -831,6 +705,22 @@ asmb(void) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); + } + break; + case Hplan9x64: + asmplan9sym(); + cflush(); + + sym = lookup("pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); } break; case Hwindows: @@ -848,7 +738,7 @@ asmb(void) cseek(0L); switch(HEADTYPE) { default: - case Hplan9x32: /* plan9 */ + case Hplan9x64: /* plan9 */ magic = 4*26*26+7; magic |= 0x00008000; /* fat header */ lputb(magic); /* magic */ @@ -862,7 +752,7 @@ asmb(void) lputb(lcsize); /* line offsets */ vputb(vl); /* va of entry */ break; - case Hplan9x64: /* plan9 */ + case Hplan9x32: /* plan9 */ magic = 4*26*26+7; lputb(magic); /* magic */ lputb(segtext.filelen); /* sizes */ @@ -880,257 +770,7 @@ asmb(void) case Hfreebsd: case Hnetbsd: case Hopenbsd: - /* elf amd-64 */ - - eh = getElfEhdr(); - startva = INITTEXT - HEADR; - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) { - switch(HEADTYPE) { - case Hlinux: - interpreter = linuxdynld; - break; - case Hfreebsd: - interpreter = freebsddynld; - break; - case Hnetbsd: - interpreter = netbsddynld; - break; - case Hopenbsd: - interpreter = openbsddynld; - break; - } - } - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 8; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 8; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64SYMSIZE; - sh->addralign = 8; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - if(elfverneed) { - sh = newElfShdr(elfstr[ElfStrGnuVersion]); - sh->type = SHT_GNU_VERSYM; - sh->flags = SHF_ALLOC; - sh->addralign = 2; - sh->link = dynsym; - sh->entsize = 2; - shsym(sh, lookup(".gnu.version", 0)); - - sh = newElfShdr(elfstr[ElfStrGnuVersionR]); - sh->type = SHT_GNU_VERNEED; - sh->flags = SHF_ALLOC; - sh->addralign = 8; - sh->info = elfverneed; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".gnu.version_r", 0)); - } - - sh = newElfShdr(elfstr[ElfStrRelaPlt]); - sh->type = SHT_RELA; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64RELASIZE; - sh->addralign = 8; - sh->link = dynsym; - sh->info = eh->shnum; // .plt - shsym(sh, lookup(".rela.plt", 0)); - - sh = newElfShdr(elfstr[ElfStrPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->entsize = 16; - sh->addralign = 4; - shsym(sh, lookup(".plt", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 8; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRela]); - sh->type = SHT_RELA; - sh->flags = SHF_ALLOC; - sh->entsize = ELF64RELASIZE; - sh->addralign = 8; - sh->link = dynsym; - shsym(sh, lookup(".rela", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 16; - sh->addralign = 8; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - */ - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 8; - } - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 8; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 8; - sh->entsize = 24; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - if(HEADTYPE == Hfreebsd) - eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; - else if(HEADTYPE == Hnetbsd) - eh->ident[EI_OSABI] = ELFOSABI_NETBSD; - else if(HEADTYPE == Hopenbsd) - eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; - eh->ident[EI_CLASS] = ELFCLASS64; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - - eh->type = ET_EXEC; - eh->machine = EM_X86_64; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + asmbelf(symo); break; case Hwindows: asmbpe(); @@ -1153,78 +793,3 @@ rnd(vlong v, vlong r) v -= c; return v; } - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(s=allsym; s!=S; s=s->allsym) { - if(s->hide) - continue; - switch(s->type&~SSUB) { - case SCONST: - case SRODATA: - case SSYMTAB: - case SPCLNTAB: - case SDATA: - case SNOPTRDATA: - case SELFROSECT: - case SMACHOGOT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SWINDOWS: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - if(s->np > 0) - diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - - for(s = textp; s != nil; s = s->next) { - if(s->text == nil) - continue; - - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+8, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %ud\n", symsize); - Bflush(&bso); -} diff --git a/src/cmd/6l/doc.go b/src/cmd/6l/doc.go index 4d94b209b..6287dd9be 100644 --- a/src/cmd/6l/doc.go +++ b/src/cmd/6l/doc.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. +// +build ignore + /* 6l is the linker for the x86-64. @@ -10,4 +12,4 @@ The $GOARCH for these tools is amd64. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 5f62239a1..ffb8a4552 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -40,7 +40,26 @@ enum { thechar = '6', - PtrSize = 8 + PtrSize = 8, + IntSize = 8, + + // Loop alignment constants: + // want to align loop entry to LoopAlign-byte boundary, + // and willing to insert at most MaxLoopPad bytes of NOP to do so. + // We define a loop entry as the target of a backward jump. + // + // gcc uses MaxLoopPad = 10 for its 'generic x86-64' config, + // and it aligns all jump targets, not just backward jump targets. + // + // As of 6/1/2012, the effect of setting MaxLoopPad = 10 here + // is very slight but negative, so the alignment is disabled by + // setting MaxLoopPad = 0. The code is here for reference and + // for future experiments. + // + LoopAlign = 16, + MaxLoopPad = 0, + + FuncAlign = 16 }; #define P ((Prog*)0) @@ -134,11 +153,16 @@ struct Sym int32 plt; int32 got; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in SSUB list Sym* outer; // container of sub + Sym* reachparent; + Sym* queue; vlong value; vlong size; Sym* gotype; @@ -146,6 +170,7 @@ struct Sym char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -158,6 +183,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; struct Optab { @@ -294,9 +320,10 @@ enum EXTERN int32 HEADR; EXTERN int32 HEADTYPE; EXTERN int32 INITRND; -EXTERN vlong INITTEXT; -EXTERN vlong INITDAT; +EXTERN int64 INITTEXT; +EXTERN int64 INITDAT; EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN char* pcstr; EXTERN Auto* curauto; EXTERN Auto* curhist; @@ -304,7 +331,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN vlong elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN char literal[32]; EXTERN Sym* textp; EXTERN Sym* etextp; diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 692cab7b8..10e4a9860 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -58,8 +58,8 @@ Header headers[] = { }; /* - * -Hplan9x32 -T4136 -R4096 is plan9 64-bit format - * -Hplan9 -T4128 -R4096 is plan9 32-bit format + * -Hplan9x32 -T4128 -R4096 is plan9 32-bit format + * -Hplan9 -T0x200028 -R0x200000 is plan9 64-bit format * -Helf -T0x80110000 -R4096 is ELF32 * -Hdarwin -Tx -Rx is apple MH-exec * -Hlinux -Tx -Rx is linux elf-exec @@ -67,23 +67,11 @@ Header headers[] = { * -Hnetbsd -Tx -Rx is NetBSD elf-exec * -Hopenbsd -Tx -Rx is OpenBSD elf-exec * -Hwindows -Tx -Rx is MS Windows PE32+ - * - * options used: 189BLQSWabcjlnpsvz */ void -usage(void) -{ - fprint(2, "usage: 6l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.6\n"); - exits("usage"); -} - -void main(int argc, char *argv[]) { - int c; - char *name, *val; - Binit(&bso, 1, OWRITE); listinit(); memset(debug, 0, sizeof(debug)); @@ -94,52 +82,46 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': /* output to (next arg) */ - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND + flagcount("1", "use alternate profiling code", &debug['1']); + flagcount("8", "assume 64-bit addresses", &debug['8']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagint64("D", "addr: data address", &INITDAT); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("Q", "debug byte-register code gen", &debug['Q']); + flagint32("R", "rnd: address rounding", &INITRND); + flagcount("S", "check type signatures", &debug['S']); + flagint64("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &isobj); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagcount("n", "dump symbol table", &debug['n']); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + flagcount("shared", "generate shared object", &flag_shared); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -149,6 +131,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "6.out.exe"; @@ -163,7 +154,7 @@ main(int argc, char *argv[]) diag("unknown -H option"); errorexit(); case Hplan9x32: /* plan 9 */ - HEADR = 32L+8L; + HEADR = 32L; if(INITTEXT == -1) INITTEXT = 4096+HEADR; if(INITDAT == -1) @@ -172,13 +163,13 @@ main(int argc, char *argv[]) INITRND = 4096; break; case Hplan9x64: /* plan 9 */ - HEADR = 32L; + HEADR = 32L + 8L; if(INITTEXT == -1) - INITTEXT = 4096+32; + INITTEXT = 0x200000+HEADR; if(INITDAT == -1) INITDAT = 0; if(INITRND == -1) - INITRND = 4096; + INITRND = 0x200000; break; case Helf: /* elf32 executable */ HEADR = rnd(52L+3*32L, 16); @@ -506,8 +497,6 @@ loop: p->line = Bget4(f); p->back = 2; p->mode = mode; - p->ft = 0; - p->tt = 0; zaddr(pn, f, &p->from, h); fromgotype = adrgotype; zaddr(pn, f, &p->to, h); @@ -597,6 +586,15 @@ loop: pc++; goto loop; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + goto loop; + + case ATYPE: + pc++; + goto loop; + case ATEXT: s = p->from.sym; if(s->text != nil) { @@ -640,6 +638,7 @@ loop: } s->type = STEXT; s->value = pc; + s->args = p->to.offset >> 32; lastp = p; p->pc = pc++; goto loop; @@ -677,7 +676,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -711,7 +710,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/6l/optab.c b/src/cmd/6l/optab.c index 5746ded19..a163e6faa 100644 --- a/src/cmd/6l/optab.c +++ b/src/cmd/6l/optab.c @@ -269,7 +269,7 @@ uchar yimul[] = }; uchar yimul3[] = { - Yml, Yrl, Zibm_r, 1, + Yml, Yrl, Zibm_r, 2, 0 }; uchar ybyte[] = @@ -302,6 +302,11 @@ uchar ypopl[] = Ynone, Ym, Zo_m, 2, 0 }; +uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 2, + 0, +}; uchar yscond[] = { Ynone, Ymb, Zo_m, 2, @@ -309,7 +314,9 @@ uchar yscond[] = }; uchar yjcond[] = { - Ynone, Ybr, Zbr, 1, + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, 0 }; uchar yloop[] = @@ -319,7 +326,8 @@ uchar yloop[] = }; uchar ycall[] = { - Ynone, Yml, Zo_m64, 2, + Ynone, Yml, Zo_m64, 0, + Yrx, Yrx, Zo_m64, 2, Ynone, Ybr, Zcall, 1, 0 }; @@ -511,17 +519,17 @@ uchar ymrxr[] = }; uchar ymshuf[] = { - Ymm, Ymr, Zibm_r, 1, + Ymm, Ymr, Zibm_r, 2, 0 }; uchar yxshuf[] = { - Yxm, Yxr, Zibm_r, 1, + Yxm, Yxr, Zibm_r, 2, 0 }; uchar yextrw[] = { - Yxr, Yrl, Zibm_r, 1, + Yxr, Yrl, Zibm_r, 2, 0 }; uchar ypsdq[] = @@ -539,6 +547,21 @@ uchar ycrc32l[] = { Yml, Yrl, Zlitm_r, 0, }; +uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; +uchar yaes[] = +{ + Yxm, Yxr, Zlitm_r, 2, + 0 +}; +uchar yaes2[] = +{ + Yxm, Yxr, Zibm_r, 2, + 0 +}; /* * You are doasm, holding in your hand a Prog* with p->as set to, say, ACRC32, @@ -636,6 +659,8 @@ Optab optab[] = { ABSRL, yml_rl, Pm, 0xbd }, { ABSRQ, yml_rl, Pw, 0x0f,0xbd }, { ABSRW, yml_rl, Pq, 0xbd }, + { ABSWAPL, ybswap, Px, 0x0f,0xc8 }, + { ABSWAPQ, ybswap, Pw, 0x0f,0xc8 }, { ABTCL, ybtl, Pm, 0xba,(07),0xbb }, { ABTCQ, ybtl, Pw, 0x0f,0xba,(07),0x0f,0xbb }, { ABTCW, ybtl, Pq, 0xba,(07),0xbb }, @@ -777,7 +802,7 @@ Optab optab[] = { AIMULL, yimul, Px, 0xf7,(05),0x6b,0x69,Pm,0xaf }, { AIMULQ, yimul, Pw, 0xf7,(05),0x6b,0x69,Pm,0xaf }, { AIMULW, yimul, Pe, 0xf7,(05),0x6b,0x69,Pm,0xaf }, - { AIMUL3Q, yimul3, Pw, 0x6b }, + { AIMUL3Q, yimul3, Pw, 0x6b,(00) }, { AINB, yin, Pb, 0xe4,0xec }, { AINCB, yincb, Pb, 0xfe,(00) }, { AINCL, yincl, Px, 0xff,(00) }, @@ -936,7 +961,7 @@ Optab optab[] = { APCMPGTB, ymm, Py, 0x64,Pe,0x64 }, { APCMPGTL, ymm, Py, 0x66,Pe,0x66 }, { APCMPGTW, ymm, Py, 0x65,Pe,0x65 }, - { APEXTRW, yextrw, Pq, 0xc5 }, + { APEXTRW, yextrw, Pq, 0xc5,(00) }, { APF2IL, ymfp, Px, 0x1d }, { APF2IW, ymfp, Px, 0x1c }, { API2FL, ymfp, Px, 0x0d }, @@ -957,7 +982,7 @@ Optab optab[] = { APFRSQRT, ymfp, Px, 0x97 }, { APFSUB, ymfp, Px, 0x9a }, { APFSUBR, ymfp, Px, 0xaa }, - { APINSRW, yextrw, Pq, 0xc4 }, + { APINSRW, yextrw, Pq, 0xc4,(00) }, { APMADDWL, ymm, Py, 0xf5,Pe,0xf5 }, { APMAXSW, yxm, Pe, 0xee }, { APMAXUB, yxm, Pe, 0xde }, @@ -979,10 +1004,10 @@ Optab optab[] = { APOPW, ypopl, Pe, 0x58,0x8f,(00) }, { APOR, ymm, Py, 0xeb,Pe,0xeb }, { APSADBW, yxm, Pq, 0xf6 }, - { APSHUFHW, yxshuf, Pf3, 0x70 }, - { APSHUFL, yxshuf, Pq, 0x70 }, - { APSHUFLW, yxshuf, Pf2, 0x70 }, - { APSHUFW, ymshuf, Pm, 0x70 }, + { APSHUFHW, yxshuf, Pf3, 0x70,(00) }, + { APSHUFL, yxshuf, Pq, 0x70,(00) }, + { APSHUFLW, yxshuf, Pf2, 0x70,(00) }, + { APSHUFW, ymshuf, Pm, 0x70,(00) }, { APSLLO, ypsdq, Pq, 0x73,(07) }, { APSLLL, yps, Py, 0xf2, 0x72,(06), Pe,0xf2, Pe,0x72,(06) }, { APSLLQ, yps, Py, 0xf3, 0x73,(06), Pe,0xf3, Pe,0x73,(06) }, @@ -1087,8 +1112,8 @@ Optab optab[] = { ASHRL, yshl, Px, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, { ASHRQ, yshl, Pw, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, { ASHRW, yshl, Pe, 0xd1,(05),0xc1,(05),0xd3,(05),0xd3,(05) }, - { ASHUFPD, yxshuf, Pq, 0xc6 }, - { ASHUFPS, yxshuf, Pm, 0xc6 }, + { ASHUFPD, yxshuf, Pq, 0xc6,(00) }, + { ASHUFPS, yxshuf, Pm, 0xc6,(00) }, { ASQRTPD, yxm, Pe, 0x51 }, { ASQRTPS, yxm, Pm, 0x51 }, { ASQRTSD, yxm, Pf2, 0x51 }, @@ -1270,8 +1295,30 @@ Optab optab[] = { AXADDQ, yrl_ml, Pw, 0x0f,0xc1 }, { AXADDW, yrl_ml, Pe, 0x0f,0xc1 }, - { ACRC32B, ycrc32l,Px, 0xf2,0x0f,0x38,0xf0,0}, - { ACRC32Q, ycrc32l,Pw, 0xf2,0x0f,0x38,0xf1,0}, + { ACRC32B, ycrc32l,Px, 0xf2,0x0f,0x38,0xf0,0 }, + { ACRC32Q, ycrc32l,Pw, 0xf2,0x0f,0x38,0xf1,0 }, + + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { AMOVQL, yrl_ml, Px, 0x89 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AAESENC, yaes, Pq, 0x38,0xdc,(0) }, + { AAESENCLAST, yaes, Pq, 0x38,0xdd,(0) }, + { AAESDEC, yaes, Pq, 0x38,0xde,(0) }, + { AAESDECLAST, yaes, Pq, 0x38,0xdf,(0) }, + { AAESIMC, yaes, Pq, 0x38,0xdb,(0) }, + { AAESKEYGENASSIST, yaes2, Pq, 0x3a,0xdf,(0) }, + + { APSHUFD, yaes2, Pq, 0x70,(0) }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ALOCALS }, + { ATYPE }, { AEND }, 0 diff --git a/src/cmd/6l/pass.c b/src/cmd/6l/pass.c index c9b477627..0054b329f 100644 --- a/src/cmd/6l/pass.c +++ b/src/cmd/6l/pass.c @@ -79,6 +79,7 @@ nofollow(int a) case ARETFL: case ARETFQ: case ARETFW: + case AUNDEF: return 1; } return 0; @@ -192,20 +193,34 @@ loop: * recurse to follow one path. * continue loop on the other. */ - q = brchain(p->link); - if(q != P && q->mark) - if(a != ALOOP) { - p->as = relinv(a); - p->link = p->pcond; + if((q = brchain(p->pcond)) != P) p->pcond = q; + if((q = brchain(p->link)) != P) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } } xfol(p->link, last); - q = brchain(p->pcond); - if(q->mark) { - p->pcond = q; + if(p->pcond->mark) return; - } - p = q; + p = p->pcond; goto loop; } p = p->link; @@ -295,7 +310,8 @@ patch(void) } } if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) { + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd + || HEADTYPE == Hplan9x64) { // ELF uses FS instead of GS. if(p->from.type == D_INDIR+D_GS) p->from.type = D_INDIR+D_FS; @@ -307,7 +323,7 @@ patch(void) if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - if((s->type&~SSUB) != STEXT) { + if((s->type&SMASK) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; @@ -405,7 +421,7 @@ dostkoff(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { if(cursym->text == nil || cursym->text->link == nil) - continue; + continue; p = cursym->text; parsetextconst(p->to.offset); @@ -413,6 +429,14 @@ dostkoff(void) if(autoffset < 0) autoffset = 0; + if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) { + for(q = p; q != P; q = q->link) + if(q->as == ACALL) + goto noleaf; + p->from.scale |= NOSPLIT; + noleaf:; + } + q = P; if((p->from.scale & NOSPLIT) && autoffset >= StackSmall) diag("nosplit func likely to overflow stack"); @@ -421,7 +445,8 @@ dostkoff(void) p = appendp(p); // load g into CX p->as = AMOVQ; if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd - || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) // ELF uses FS + || HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd + || HEADTYPE == Hplan9x64) // ELF uses FS p->from.type = D_INDIR+D_FS; else p->from.type = D_INDIR+D_GS; @@ -579,6 +604,16 @@ dostkoff(void) p->spadj = autoffset; if(q != P) q->pcond = p; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(p); + p->as = ANOP; + p->spadj = -PtrSize; + p = appendp(p); + p->as = ANOP; + p->spadj = PtrSize; } deltasp = autoffset; @@ -618,6 +653,34 @@ dostkoff(void) q1->pcond = p; } + if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 6l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = autoffset/8; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVQ; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(p); + p->as = AREP; + + p = appendp(p); + p->as = ASTOSQ; + } + for(; p != P; p = p->link) { pcsize = p->mode/8; a = p->from.type; diff --git a/src/cmd/6l/span.c b/src/cmd/6l/span.c index 28eb38f40..283a0e349 100644 --- a/src/cmd/6l/span.c +++ b/src/cmd/6l/span.c @@ -37,6 +37,37 @@ static int rexflag; static int asmode; static vlong vaddr(Adr*, Reloc*); +// single-instruction no-ops of various lengths. +// constructed by hand and disassembled with gdb to verify. +// see http://www.agner.org/optimize/optimizing_assembly.pdf for discussion. +static uchar nop[][16] = { + {0x90}, + {0x66, 0x90}, + {0x0F, 0x1F, 0x00}, + {0x0F, 0x1F, 0x40, 0x00}, + {0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00}, + {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00}, + {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x66, 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void +fillnop(uchar *p, int n) +{ + int m; + + while(n > 0) { + m = n; + if(m > nelem(nop)) + m = nelem(nop); + memmove(p, nop[m-1], m); + p += m; + n -= m; + } +} + void span1(Sym *s) { @@ -52,8 +83,10 @@ span1(Sym *s) for(p = s->text; p != P; p = p->link) { p->back = 2; // use short branches first time through - if((q = p->pcond) != P && (q->back & 2)) + if((q = p->pcond) != P && (q->back & 2)) { p->back |= 1; // backward jump + q->back |= 4; // loop head + } if(p->as == AADJSP) { p->to.type = D_SP; @@ -78,6 +111,16 @@ span1(Sym *s) s->np = 0; c = 0; for(p = s->text; p != P; p = p->link) { + if((p->back & 4) && (c&(LoopAlign-1)) != 0) { + // pad with NOPs + v = -c&(LoopAlign-1); + if(v <= MaxLoopPad) { + symgrow(s, c+v); + fillnop(s->p+c, v); + c += v; + } + } + p->pc = c; // process forward jumps to p @@ -329,7 +372,10 @@ oclass(Adr *a) switch(a->index) { case D_EXTERN: case D_STATIC: - return Yi32; /* TO DO: Yi64 */ + if(flag_shared) + return Yiauto; + else + return Yi32; /* TO DO: Yi64 */ case D_AUTO: case D_PARAM: return Yiauto; @@ -688,7 +734,10 @@ vaddr(Adr *a, Reloc *r) diag("need reloc for %D", a); errorexit(); } - r->type = D_ADDR; + if(flag_shared) + r->type = D_PCREL; + else + r->type = D_ADDR; r->siz = 4; // TODO: 8 for external symbols r->off = -1; // caller must fill in r->sym = s; @@ -717,6 +766,8 @@ asmandsz(Adr *a, int r, int rex, int m64) goto bad; case D_STATIC: case D_EXTERN: + if(flag_shared) + goto bad; t = D_NONE; v = vaddr(a, &rel); break; @@ -777,7 +828,7 @@ asmandsz(Adr *a, int r, int rex, int m64) rexflag |= (regrex[t] & Rxb) | rex; if(t == D_NONE || (D_CS <= t && t <= D_GS)) { - if(asmode != 64){ + if(flag_shared && t == D_NONE && (a->type == D_STATIC || a->type == D_EXTERN) || asmode != 64) { *andptr++ = (0 << 6) | (5 << 0) | (r << 3); goto putrelv; } @@ -1204,7 +1255,8 @@ found: break; case Zibm_r: - *andptr++ = op; + while ((op = o->op[z++]) != 0) + *andptr++ = op; asmand(&p->from, &p->to); *andptr++ = p->to.offset; break; @@ -1574,7 +1626,9 @@ bad: pp = *p; z = p->from.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->to)) { + if(isax(&p->to) || p->to.type == D_NONE) { + // We certainly don't want to exchange + // with AX if the op is MUL or DIV. *andptr++ = 0x87; /* xchg lhs,bx */ asmando(&p->from, reg[D_BX]); subreg(&pp, z, D_BX); @@ -1730,13 +1784,17 @@ asmins(Prog *p) if(c != 0xf2 && c != 0xf3 && (c < 0x64 || c > 0x67) && c != 0x2e && c != 0x3e && c != 0x26) break; } - for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { - if(r->off < p->pc) - break; - r->off++; - } memmove(and+np+1, and+np, n-np); and[np] = 0x40 | rexflag; andptr++; } + n = andptr - and; + for(r=cursym->r+cursym->nr; r-- > cursym->r; ) { + if(r->off < p->pc) + break; + if(rexflag) + r->off++; + if(r->type == D_PCREL) + r->add -= p->pc + n - (r->off + r->siz); + } } diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index f1881808f..60707d1c9 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -53,9 +53,9 @@ %left '+' '-' %left '*' '/' '%' %token <lval> LTYPE0 LTYPE1 LTYPE2 LTYPE3 LTYPE4 -%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG +%token <lval> LTYPEC LTYPED LTYPEN LTYPER LTYPET LTYPES LTYPEM LTYPEI LTYPEG LTYPEXC %token <lval> LCONST LFP LPC LSB -%token <lval> LBREG LLREG LSREG LFREG +%token <lval> LBREG LLREG LSREG LFREG LXREG %token <dval> LFCONST %token <sval> LSCONST LSP %token <sym> LNAME LLAB LVAR @@ -63,7 +63,7 @@ %type <con2> con2 %type <gen> mem imm imm2 reg nam rel rem rim rom omem nmem %type <gen2> nonnon nonrel nonrem rimnon rimrem remrim -%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 +%type <gen2> spec1 spec2 spec3 spec4 spec5 spec6 spec7 spec8 spec9 %% prog: | prog @@ -116,6 +116,7 @@ inst: | LTYPEM spec6 { outcode($1, &$2); } | LTYPEI spec7 { outcode($1, &$2); } | LTYPEG spec8 { outcode($1, &$2); } +| LTYPEXC spec9 { outcode($1, &$2); } nonnon: { @@ -177,6 +178,11 @@ nonrel: $$.from = nullgen; $$.to = $1; } +| imm ',' rel + { + $$.from = $1; + $$.to = $3; + } spec1: /* DATA */ nam '/' con ',' imm @@ -282,6 +288,14 @@ spec8: /* GLOBL */ $$.to = $5; } +spec9: /* CMPPS/CMPPD */ + reg ',' rem ',' con + { + $$.from = $1; + $$.to = $3; + $$.to.offset = $5; + } + rem: reg | mem @@ -345,6 +359,11 @@ reg: $$ = nullgen; $$.type = $1; } +| LXREG + { + $$ = nullgen; + $$.type = $1; + } | LSP { $$ = nullgen; diff --git a/src/cmd/8a/doc.go b/src/cmd/8a/doc.go index a43b4461f..737c56f13 100644 --- a/src/cmd/8a/doc.go +++ b/src/cmd/8a/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8a is a version of the Plan 9 assembler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2a + http://plan9.bell-labs.com/magic/man2html/1/8a Its target architecture is the x86, referred to by these tools for historical reasons as 386. */ -package documentation +package main diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index 1cc6e591d..770ca5a88 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -210,6 +210,15 @@ struct "F6", LFREG, D_F0+6, "F7", LFREG, D_F0+7, + "X0", LXREG, D_X0+0, + "X1", LXREG, D_X0+1, + "X2", LXREG, D_X0+2, + "X3", LXREG, D_X0+3, + "X4", LXREG, D_X0+4, + "X5", LXREG, D_X0+5, + "X6", LXREG, D_X0+6, + "X7", LXREG, D_X0+7, + "CS", LSREG, D_CS, "SS", LSREG, D_SS, "DS", LSREG, D_DS, @@ -271,6 +280,7 @@ struct "BSFW", LTYPE3, ABSFW, "BSRL", LTYPE3, ABSRL, "BSRW", LTYPE3, ABSRW, + "BSWAPL", LTYPE1, ABSWAPL, "BTCL", LTYPE3, ABTCL, "BTCW", LTYPE3, ABTCW, "BTL", LTYPE3, ABTL, @@ -667,6 +677,120 @@ struct "MFENCE", LTYPE0, AMFENCE, "SFENCE", LTYPE0, ASFENCE, "EMMS", LTYPE0, AEMMS, + "PREFETCHT0", LTYPE2, APREFETCHT0, + "PREFETCHT1", LTYPE2, APREFETCHT1, + "PREFETCHT2", LTYPE2, APREFETCHT2, + "PREFETCHNTA", LTYPE2, APREFETCHNTA, + "UNDEF", LTYPE0, AUNDEF, + + "ADDPD", LTYPE3, AADDPD, + "ADDPS", LTYPE3, AADDPS, + "ADDSD", LTYPE3, AADDSD, + "ADDSS", LTYPE3, AADDSS, + "ANDNPD", LTYPE3, AANDNPD, + "ANDNPS", LTYPE3, AANDNPS, + "ANDPD", LTYPE3, AANDPD, + "ANDPS", LTYPE3, AANDPS, + "CMPPD", LTYPEXC,ACMPPD, + "CMPPS", LTYPEXC,ACMPPS, + "CMPSD", LTYPEXC,ACMPSD, + "CMPSS", LTYPEXC,ACMPSS, + "COMISD", LTYPE3, ACOMISD, + "COMISS", LTYPE3, ACOMISS, + "CVTPL2PD", LTYPE3, ACVTPL2PD, + "CVTPL2PS", LTYPE3, ACVTPL2PS, + "CVTPD2PL", LTYPE3, ACVTPD2PL, + "CVTPD2PS", LTYPE3, ACVTPD2PS, + "CVTPS2PL", LTYPE3, ACVTPS2PL, + "CVTPS2PD", LTYPE3, ACVTPS2PD, + "CVTSD2SL", LTYPE3, ACVTSD2SL, + "CVTSD2SS", LTYPE3, ACVTSD2SS, + "CVTSL2SD", LTYPE3, ACVTSL2SD, + "CVTSL2SS", LTYPE3, ACVTSL2SS, + "CVTSS2SD", LTYPE3, ACVTSS2SD, + "CVTSS2SL", LTYPE3, ACVTSS2SL, + "CVTTPD2PL", LTYPE3, ACVTTPD2PL, + "CVTTPS2PL", LTYPE3, ACVTTPS2PL, + "CVTTSD2SL", LTYPE3, ACVTTSD2SL, + "CVTTSS2SL", LTYPE3, ACVTTSS2SL, + "DIVPD", LTYPE3, ADIVPD, + "DIVPS", LTYPE3, ADIVPS, + "DIVSD", LTYPE3, ADIVSD, + "DIVSS", LTYPE3, ADIVSS, + "MASKMOVOU", LTYPE3, AMASKMOVOU, + "MASKMOVDQU", LTYPE3, AMASKMOVOU, /* syn */ + "MAXPD", LTYPE3, AMAXPD, + "MAXPS", LTYPE3, AMAXPS, + "MAXSD", LTYPE3, AMAXSD, + "MAXSS", LTYPE3, AMAXSS, + "MINPD", LTYPE3, AMINPD, + "MINPS", LTYPE3, AMINPS, + "MINSD", LTYPE3, AMINSD, + "MINSS", LTYPE3, AMINSS, + "MOVAPD", LTYPE3, AMOVAPD, + "MOVAPS", LTYPE3, AMOVAPS, + "MOVO", LTYPE3, AMOVO, + "MOVOA", LTYPE3, AMOVO, /* syn */ + "MOVOU", LTYPE3, AMOVOU, + "MOVHLPS", LTYPE3, AMOVHLPS, + "MOVHPD", LTYPE3, AMOVHPD, + "MOVHPS", LTYPE3, AMOVHPS, + "MOVLHPS", LTYPE3, AMOVLHPS, + "MOVLPD", LTYPE3, AMOVLPD, + "MOVLPS", LTYPE3, AMOVLPS, + "MOVMSKPD", LTYPE3, AMOVMSKPD, + "MOVMSKPS", LTYPE3, AMOVMSKPS, + "MOVNTO", LTYPE3, AMOVNTO, + "MOVNTDQ", LTYPE3, AMOVNTO, /* syn */ + "MOVNTPD", LTYPE3, AMOVNTPD, + "MOVNTPS", LTYPE3, AMOVNTPS, + "MOVSD", LTYPE3, AMOVSD, + "MOVSS", LTYPE3, AMOVSS, + "MOVUPD", LTYPE3, AMOVUPD, + "MOVUPS", LTYPE3, AMOVUPS, + "MULPD", LTYPE3, AMULPD, + "MULPS", LTYPE3, AMULPS, + "MULSD", LTYPE3, AMULSD, + "MULSS", LTYPE3, AMULSS, + "ORPD", LTYPE3, AORPD, + "ORPS", LTYPE3, AORPS, + "PADDQ", LTYPE3, APADDQ, + "PMAXSW", LTYPE3, APMAXSW, + "PMAXUB", LTYPE3, APMAXUB, + "PMINSW", LTYPE3, APMINSW, + "PMINUB", LTYPE3, APMINUB, + "PSADBW", LTYPE3, APSADBW, + "PSUBB", LTYPE3, APSUBB, + "PSUBL", LTYPE3, APSUBL, + "PSUBQ", LTYPE3, APSUBQ, + "PSUBSB", LTYPE3, APSUBSB, + "PSUBSW", LTYPE3, APSUBSW, + "PSUBUSB", LTYPE3, APSUBUSB, + "PSUBUSW", LTYPE3, APSUBUSW, + "PSUBW", LTYPE3, APSUBW, + "PUNPCKHQDQ", LTYPE3, APUNPCKHQDQ, + "PUNPCKLQDQ", LTYPE3, APUNPCKLQDQ, + "RCPPS", LTYPE3, ARCPPS, + "RCPSS", LTYPE3, ARCPSS, + "RSQRTPS", LTYPE3, ARSQRTPS, + "RSQRTSS", LTYPE3, ARSQRTSS, + "SQRTPD", LTYPE3, ASQRTPD, + "SQRTPS", LTYPE3, ASQRTPS, + "SQRTSD", LTYPE3, ASQRTSD, + "SQRTSS", LTYPE3, ASQRTSS, + "SUBPD", LTYPE3, ASUBPD, + "SUBPS", LTYPE3, ASUBPS, + "SUBSD", LTYPE3, ASUBSD, + "SUBSS", LTYPE3, ASUBSS, + "UCOMISD", LTYPE3, AUCOMISD, + "UCOMISS", LTYPE3, AUCOMISS, + "UNPCKHPD", LTYPE3, AUNPCKHPD, + "UNPCKHPS", LTYPE3, AUNPCKHPS, + "UNPCKLPD", LTYPE3, AUNPCKLPD, + "UNPCKLPS", LTYPE3, AUNPCKLPS, + "XORPD", LTYPE3, AXORPD, + "XORPS", LTYPE3, AXORPS, + "USEFIELD", LTYPEN, AUSEFIELD, 0 }; @@ -915,11 +1039,38 @@ outhist(void) Hist *h; char *p, *q, *op, c; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + + tofree = nil; g = nullgen; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -971,6 +1122,11 @@ outhist(void) Bputc(&obuf, h->line>>24); zaddr(&nullgen, 0); zaddr(&g, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } diff --git a/src/cmd/8a/y.tab.c b/src/cmd/8a/y.tab.c index ccd3a2563..38f2de52d 100644 --- a/src/cmd/8a/y.tab.c +++ b/src/cmd/8a/y.tab.c @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.6.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton implementation for Bison's Yacc-like parsers in C +/* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,7 +44,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.4.1" +#define YYBISON_VERSION "2.6.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -60,14 +58,11 @@ /* Pull parsers. */ #define YYPULL 1 -/* Using locations. */ -#define YYLSP_NEEDED 0 /* Copy the first part of user declarations. */ - -/* Line 189 of yacc.c */ +/* Line 360 of yacc.c */ #line 31 "a.y" #include <u.h> @@ -75,14 +70,16 @@ #include <libc.h> #include "a.h" +/* Line 360 of yacc.c */ +#line 75 "y.tab.c" -/* Line 189 of yacc.c */ -#line 81 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif +# ifndef YY_NULL +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULL nullptr +# else +# define YY_NULL 0 +# endif +# endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE @@ -92,11 +89,17 @@ # define YYERROR_VERBOSE 0 #endif -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 +/* In a future release of Bison, this section will be replaced + by #include "y.tab.h". */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; #endif - /* Tokens. */ #ifndef YYTOKENTYPE @@ -118,20 +121,22 @@ LTYPEM = 269, LTYPEI = 270, LTYPEG = 271, - LCONST = 272, - LFP = 273, - LPC = 274, - LSB = 275, - LBREG = 276, - LLREG = 277, - LSREG = 278, - LFREG = 279, - LFCONST = 280, - LSCONST = 281, - LSP = 282, - LNAME = 283, - LLAB = 284, - LVAR = 285 + LTYPEXC = 272, + LCONST = 273, + LFP = 274, + LPC = 275, + LSB = 276, + LBREG = 277, + LLREG = 278, + LSREG = 279, + LFREG = 280, + LXREG = 281, + LFCONST = 282, + LSCONST = 283, + LSP = 284, + LNAME = 285, + LLAB = 286, + LVAR = 287 }; #endif /* Tokens. */ @@ -149,29 +154,29 @@ #define LTYPEM 269 #define LTYPEI 270 #define LTYPEG 271 -#define LCONST 272 -#define LFP 273 -#define LPC 274 -#define LSB 275 -#define LBREG 276 -#define LLREG 277 -#define LSREG 278 -#define LFREG 279 -#define LFCONST 280 -#define LSCONST 281 -#define LSP 282 -#define LNAME 283 -#define LLAB 284 -#define LVAR 285 - +#define LTYPEXC 272 +#define LCONST 273 +#define LFP 274 +#define LPC 275 +#define LSB 276 +#define LBREG 277 +#define LLREG 278 +#define LSREG 279 +#define LFREG 280 +#define LXREG 281 +#define LFCONST 282 +#define LSCONST 283 +#define LSP 284 +#define LNAME 285 +#define LLAB 286 +#define LVAR 287 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { - -/* Line 214 of yacc.c */ +/* Line 376 of yacc.c */ #line 37 "a.y" Sym *sym; @@ -186,21 +191,36 @@ typedef union YYSTYPE Gen2 gen2; - -/* Line 214 of yacc.c */ -#line 192 "y.tab.c" +/* Line 376 of yacc.c */ +#line 196 "y.tab.c" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif +extern YYSTYPE yylval; -/* Copy the second part of user declarations. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ +/* Copy the second part of user declarations. */ -/* Line 264 of yacc.c */ -#line 204 "y.tab.c" +/* Line 379 of yacc.c */ +#line 224 "y.tab.c" #ifdef short # undef short @@ -250,27 +270,27 @@ typedef short int yytype_int16; #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ -# if YYENABLE_NLS +# if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include <libintl.h> /* INFRINGES ON USER NAME SPACE */ -# define YY_(msgid) dgettext ("bison-runtime", msgid) +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ -# define YY_(msgid) msgid +# define YY_(Msgid) Msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ -# define YYUSE(e) ((void) (e)) +# define YYUSE(E) ((void) (E)) #else -# define YYUSE(e) /* empty */ +# define YYUSE(E) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint -# define YYID(n) (n) +# define YYID(N) (N) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) @@ -303,11 +323,12 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # endif @@ -330,24 +351,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined _STDLIB_H \ +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef _STDLIB_H -# define _STDLIB_H 1 +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -376,23 +397,7 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -/* Copy COUNT objects from FROM to TO. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(To, From, Count) \ - __builtin_memcpy (To, From, (Count) * sizeof (*(From))) -# else -# define YYCOPY(To, From, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (To)[yyi] = (From)[yyi]; \ - } \ - while (YYID (0)) -# endif -# endif +# define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of @@ -412,23 +417,43 @@ union yyalloc #endif +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + /* YYFINAL -- State number of the termination state. */ #define YYFINAL 2 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 483 +#define YYLAST 544 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 49 +#define YYNTOKENS 51 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 37 +#define YYNNTS 38 /* YYNRULES -- Number of rules. */ -#define YYNRULES 124 +#define YYNRULES 128 /* YYNRULES -- Number of states. */ -#define YYNSTATES 244 +#define YYNSTATES 255 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 285 +#define YYMAXUTOK 287 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -439,16 +464,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 47, 12, 5, 2, - 45, 46, 10, 8, 44, 9, 2, 11, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 41, 42, - 6, 43, 7, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 49, 12, 5, 2, + 47, 48, 10, 8, 46, 9, 2, 11, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 43, 44, + 6, 45, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 2, 48, 2, 2, 2, + 2, 2, 2, 2, 3, 2, 50, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -464,7 +489,7 @@ static const yytype_uint8 yytranslate[] = 2, 2, 2, 2, 2, 2, 1, 2, 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 + 35, 36, 37, 38, 39, 40, 41, 42 }; #if YYDEBUG @@ -474,64 +499,66 @@ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 4, 5, 9, 10, 15, 16, 21, 23, 26, 29, 33, 37, 40, 43, 46, 49, 52, - 55, 58, 61, 64, 67, 70, 73, 76, 79, 80, - 82, 86, 90, 93, 95, 98, 100, 103, 105, 111, - 115, 121, 124, 126, 129, 131, 133, 137, 143, 147, - 153, 156, 158, 162, 166, 172, 174, 176, 178, 180, - 183, 186, 188, 190, 192, 194, 196, 201, 204, 207, - 209, 211, 213, 215, 217, 220, 223, 226, 229, 234, - 240, 244, 247, 249, 252, 256, 261, 263, 265, 267, - 272, 277, 284, 294, 298, 302, 307, 313, 322, 324, - 331, 337, 345, 346, 349, 352, 354, 356, 358, 360, - 362, 365, 368, 371, 375, 377, 381, 385, 389, 393, - 397, 402, 407, 411, 415 + 55, 58, 61, 64, 67, 70, 73, 76, 79, 82, + 83, 85, 89, 93, 96, 98, 101, 103, 106, 108, + 112, 118, 122, 128, 131, 133, 136, 138, 140, 144, + 150, 154, 160, 163, 165, 169, 173, 179, 185, 187, + 189, 191, 193, 196, 199, 201, 203, 205, 207, 209, + 214, 217, 220, 222, 224, 226, 228, 230, 232, 235, + 238, 241, 244, 249, 255, 259, 262, 264, 267, 271, + 276, 278, 280, 282, 287, 292, 299, 309, 313, 317, + 322, 328, 337, 339, 346, 352, 360, 361, 364, 367, + 369, 371, 373, 375, 377, 380, 383, 386, 390, 392, + 396, 400, 404, 408, 412, 417, 422, 426, 430 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { - 50, 0, -1, -1, -1, 50, 51, 52, -1, -1, - 39, 41, 53, 52, -1, -1, 38, 41, 54, 52, - -1, 42, -1, 55, 42, -1, 1, 42, -1, 38, - 43, 85, -1, 40, 43, 85, -1, 13, 56, -1, - 14, 60, -1, 15, 59, -1, 16, 57, -1, 17, - 58, -1, 21, 61, -1, 19, 62, -1, 22, 63, - -1, 18, 64, -1, 20, 65, -1, 23, 66, -1, - 24, 67, -1, 25, 68, -1, 26, 69, -1, -1, - 44, -1, 72, 44, 70, -1, 70, 44, 72, -1, - 72, 44, -1, 72, -1, 44, 70, -1, 70, -1, - 44, 73, -1, 73, -1, 81, 11, 84, 44, 75, - -1, 78, 44, 76, -1, 78, 44, 84, 44, 76, - -1, 44, 71, -1, 71, -1, 10, 81, -1, 56, - -1, 60, -1, 72, 44, 70, -1, 72, 44, 70, - 41, 32, -1, 72, 44, 70, -1, 72, 44, 70, - 41, 33, -1, 72, 44, -1, 72, -1, 72, 44, - 70, -1, 78, 44, 75, -1, 78, 44, 84, 44, - 75, -1, 74, -1, 78, -1, 73, -1, 80, -1, - 10, 74, -1, 10, 79, -1, 74, -1, 79, -1, - 75, -1, 70, -1, 75, -1, 84, 45, 29, 46, - -1, 38, 82, -1, 39, 82, -1, 31, -1, 34, - -1, 32, -1, 37, -1, 33, -1, 47, 84, -1, - 47, 81, -1, 47, 36, -1, 47, 35, -1, 47, - 45, 35, 46, -1, 47, 45, 9, 35, 46, -1, - 47, 9, 35, -1, 47, 77, -1, 27, -1, 9, - 27, -1, 27, 9, 27, -1, 9, 27, 9, 27, - -1, 79, -1, 80, -1, 84, -1, 84, 45, 32, - 46, -1, 84, 45, 37, 46, -1, 84, 45, 32, - 10, 84, 46, -1, 84, 45, 32, 46, 45, 32, - 10, 84, 46, -1, 45, 32, 46, -1, 45, 37, - 46, -1, 84, 45, 33, 46, -1, 45, 32, 10, - 84, 46, -1, 45, 32, 46, 45, 32, 10, 84, - 46, -1, 81, -1, 81, 45, 32, 10, 84, 46, - -1, 38, 82, 45, 83, 46, -1, 38, 6, 7, - 82, 45, 30, 46, -1, -1, 8, 84, -1, 9, - 84, -1, 30, -1, 37, -1, 28, -1, 27, -1, - 40, -1, 9, 84, -1, 8, 84, -1, 48, 84, - -1, 45, 85, 46, -1, 84, -1, 85, 8, 85, - -1, 85, 9, 85, -1, 85, 10, 85, -1, 85, - 11, 85, -1, 85, 12, 85, -1, 85, 6, 6, - 85, -1, 85, 7, 7, 85, -1, 85, 5, 85, - -1, 85, 4, 85, -1, 85, 3, 85, -1 + 52, 0, -1, -1, -1, 52, 53, 54, -1, -1, + 41, 43, 55, 54, -1, -1, 40, 43, 56, 54, + -1, 44, -1, 57, 44, -1, 1, 44, -1, 40, + 45, 88, -1, 42, 45, 88, -1, 13, 58, -1, + 14, 62, -1, 15, 61, -1, 16, 59, -1, 17, + 60, -1, 21, 63, -1, 19, 64, -1, 22, 65, + -1, 18, 66, -1, 20, 67, -1, 23, 68, -1, + 24, 69, -1, 25, 70, -1, 26, 71, -1, 27, + 72, -1, -1, 46, -1, 75, 46, 73, -1, 73, + 46, 75, -1, 75, 46, -1, 75, -1, 46, 73, + -1, 73, -1, 46, 76, -1, 76, -1, 78, 46, + 76, -1, 84, 11, 87, 46, 78, -1, 81, 46, + 79, -1, 81, 46, 87, 46, 79, -1, 46, 74, + -1, 74, -1, 10, 84, -1, 58, -1, 62, -1, + 75, 46, 73, -1, 75, 46, 73, 43, 33, -1, + 75, 46, 73, -1, 75, 46, 73, 43, 34, -1, + 75, 46, -1, 75, -1, 75, 46, 73, -1, 81, + 46, 78, -1, 81, 46, 87, 46, 78, -1, 77, + 46, 73, 46, 87, -1, 77, -1, 81, -1, 76, + -1, 83, -1, 10, 77, -1, 10, 82, -1, 77, + -1, 82, -1, 78, -1, 73, -1, 78, -1, 87, + 47, 30, 48, -1, 40, 85, -1, 41, 85, -1, + 32, -1, 35, -1, 33, -1, 36, -1, 39, -1, + 34, -1, 49, 87, -1, 49, 84, -1, 49, 38, + -1, 49, 37, -1, 49, 47, 37, 48, -1, 49, + 47, 9, 37, 48, -1, 49, 9, 37, -1, 49, + 80, -1, 28, -1, 9, 28, -1, 28, 9, 28, + -1, 9, 28, 9, 28, -1, 82, -1, 83, -1, + 87, -1, 87, 47, 33, 48, -1, 87, 47, 39, + 48, -1, 87, 47, 33, 10, 87, 48, -1, 87, + 47, 33, 48, 47, 33, 10, 87, 48, -1, 47, + 33, 48, -1, 47, 39, 48, -1, 87, 47, 34, + 48, -1, 47, 33, 10, 87, 48, -1, 47, 33, + 48, 47, 33, 10, 87, 48, -1, 84, -1, 84, + 47, 33, 10, 87, 48, -1, 40, 85, 47, 86, + 48, -1, 40, 6, 7, 85, 47, 31, 48, -1, + -1, 8, 87, -1, 9, 87, -1, 31, -1, 39, + -1, 29, -1, 28, -1, 42, -1, 9, 87, -1, + 8, 87, -1, 50, 87, -1, 47, 88, 48, -1, + 87, -1, 88, 8, 88, -1, 88, 9, 88, -1, + 88, 10, 88, -1, 88, 11, 88, -1, 88, 12, + 88, -1, 88, 6, 6, 88, -1, 88, 7, 7, + 88, -1, 88, 5, 88, -1, 88, 4, 88, -1, + 88, 3, 88, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -539,21 +566,21 @@ static const yytype_uint16 yyrline[] = { 0, 68, 68, 70, 69, 77, 76, 84, 83, 89, 90, 91, 94, 99, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 121, 125, - 132, 139, 146, 151, 158, 163, 170, 175, 182, 190, - 195, 203, 208, 213, 222, 223, 226, 231, 241, 246, - 256, 261, 266, 273, 278, 286, 287, 290, 291, 292, - 296, 300, 301, 302, 305, 306, 309, 315, 324, 333, - 338, 343, 348, 353, 360, 366, 377, 383, 389, 395, - 401, 409, 418, 423, 428, 433, 440, 441, 444, 450, - 456, 462, 471, 480, 485, 490, 496, 504, 514, 518, - 527, 534, 543, 546, 550, 556, 557, 561, 564, 565, - 569, 573, 577, 581, 587, 588, 592, 596, 600, 604, - 608, 612, 616, 620, 624 + 111, 112, 113, 114, 115, 116, 117, 118, 119, 122, + 126, 133, 140, 147, 152, 159, 164, 171, 176, 181, + 188, 196, 201, 209, 214, 219, 228, 229, 232, 237, + 247, 252, 262, 267, 272, 279, 284, 292, 300, 301, + 304, 305, 306, 310, 314, 315, 316, 319, 320, 323, + 329, 338, 347, 352, 357, 362, 367, 372, 379, 385, + 396, 402, 408, 414, 420, 428, 437, 442, 447, 452, + 459, 460, 463, 469, 475, 481, 490, 499, 504, 509, + 515, 523, 533, 537, 546, 553, 562, 565, 569, 575, + 576, 580, 583, 584, 588, 592, 596, 600, 606, 607, + 611, 615, 619, 623, 627, 631, 635, 639, 643 }; #endif -#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +#if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = @@ -561,14 +588,14 @@ static const char *const yytname[] = "$end", "error", "$undefined", "'|'", "'^'", "'&'", "'<'", "'>'", "'+'", "'-'", "'*'", "'/'", "'%'", "LTYPE0", "LTYPE1", "LTYPE2", "LTYPE3", "LTYPE4", "LTYPEC", "LTYPED", "LTYPEN", "LTYPER", "LTYPET", "LTYPES", - "LTYPEM", "LTYPEI", "LTYPEG", "LCONST", "LFP", "LPC", "LSB", "LBREG", - "LLREG", "LSREG", "LFREG", "LFCONST", "LSCONST", "LSP", "LNAME", "LLAB", - "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", "'~'", - "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon", + "LTYPEM", "LTYPEI", "LTYPEG", "LTYPEXC", "LCONST", "LFP", "LPC", "LSB", + "LBREG", "LLREG", "LSREG", "LFREG", "LXREG", "LFCONST", "LSCONST", "LSP", + "LNAME", "LLAB", "LVAR", "':'", "';'", "'='", "','", "'('", "')'", "'$'", + "'~'", "$accept", "prog", "$@1", "line", "$@2", "$@3", "inst", "nonnon", "rimrem", "remrim", "rimnon", "nonrem", "nonrel", "spec1", "spec2", - "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "rem", "rom", - "rim", "rel", "reg", "imm", "imm2", "con2", "mem", "omem", "nmem", "nam", - "offset", "pointer", "con", "expr", 0 + "spec3", "spec4", "spec5", "spec6", "spec7", "spec8", "spec9", "rem", + "rom", "rim", "rel", "reg", "imm", "imm2", "con2", "mem", "omem", "nmem", + "nam", "offset", "pointer", "con", "expr", YY_NULL }; #endif @@ -581,26 +608,27 @@ static const yytype_uint16 yytoknum[] = 42, 47, 37, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 58, 59, 61, 44, 40, 41, 36, 126 + 285, 286, 287, 58, 59, 61, 44, 40, 41, 36, + 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 49, 50, 51, 50, 53, 52, 54, 52, 52, - 52, 52, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, - 57, 58, 59, 59, 60, 60, 61, 61, 62, 63, - 63, 64, 64, 64, 65, 65, 66, 66, 67, 67, - 68, 68, 68, 69, 69, 70, 70, 71, 71, 71, - 71, 71, 71, 71, 72, 72, 73, 73, 73, 74, - 74, 74, 74, 74, 75, 75, 75, 75, 75, 75, - 75, 76, 77, 77, 77, 77, 78, 78, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 80, 80, - 81, 81, 82, 82, 82, 83, 83, 83, 84, 84, - 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 85 + 0, 51, 52, 53, 52, 55, 54, 56, 54, 54, + 54, 54, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 58, + 58, 59, 60, 61, 61, 62, 62, 63, 63, 63, + 64, 65, 65, 66, 66, 66, 67, 67, 68, 68, + 69, 69, 70, 70, 70, 71, 71, 72, 73, 73, + 74, 74, 74, 74, 74, 74, 74, 75, 75, 76, + 76, 76, 77, 77, 77, 77, 77, 77, 78, 78, + 78, 78, 78, 78, 78, 79, 80, 80, 80, 80, + 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 82, 83, 83, 84, 84, 85, 85, 85, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -608,241 +636,261 @@ static const yytype_uint8 yyr2[] = { 0, 2, 0, 0, 3, 0, 4, 0, 4, 1, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, - 3, 3, 2, 1, 2, 1, 2, 1, 5, 3, - 5, 2, 1, 2, 1, 1, 3, 5, 3, 5, - 2, 1, 3, 3, 5, 1, 1, 1, 1, 2, - 2, 1, 1, 1, 1, 1, 4, 2, 2, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 4, 5, - 3, 2, 1, 2, 3, 4, 1, 1, 1, 4, - 4, 6, 9, 3, 3, 4, 5, 8, 1, 6, - 5, 7, 0, 2, 2, 1, 1, 1, 1, 1, - 2, 2, 2, 3, 1, 3, 3, 3, 3, 3, - 4, 4, 3, 3, 3 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, + 1, 3, 3, 2, 1, 2, 1, 2, 1, 3, + 5, 3, 5, 2, 1, 2, 1, 1, 3, 5, + 3, 5, 2, 1, 3, 3, 5, 5, 1, 1, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 4, + 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 4, 5, 3, 2, 1, 2, 3, 4, + 1, 1, 1, 4, 4, 6, 9, 3, 3, 4, + 5, 8, 1, 6, 5, 7, 0, 2, 2, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 1, 3, + 3, 3, 3, 3, 4, 4, 3, 3, 3 }; -/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state - STATE-NUM when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 3, 1, 0, 0, 28, 0, 0, 0, 0, - 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 9, 4, 0, 11, 29, 14, 0, 0, - 108, 69, 71, 73, 70, 72, 102, 109, 0, 0, - 0, 15, 35, 55, 56, 86, 87, 98, 88, 0, - 16, 64, 33, 65, 17, 0, 18, 0, 0, 102, - 102, 0, 22, 42, 57, 61, 63, 62, 58, 88, - 20, 0, 29, 44, 45, 23, 102, 0, 0, 19, - 37, 0, 21, 0, 24, 0, 25, 0, 26, 51, - 27, 0, 7, 0, 5, 0, 10, 111, 110, 0, - 0, 0, 0, 34, 0, 0, 114, 0, 112, 0, - 0, 0, 77, 76, 0, 75, 74, 32, 0, 0, - 59, 60, 43, 67, 68, 0, 41, 0, 0, 67, - 36, 0, 0, 0, 0, 50, 0, 0, 12, 0, - 13, 102, 103, 104, 0, 0, 93, 94, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, - 0, 0, 0, 80, 0, 0, 30, 31, 0, 0, - 0, 39, 0, 46, 48, 52, 53, 0, 8, 6, - 0, 107, 105, 106, 0, 0, 0, 124, 123, 122, - 0, 0, 115, 116, 117, 118, 119, 0, 0, 89, - 95, 90, 0, 78, 66, 0, 0, 82, 81, 0, - 0, 0, 0, 0, 100, 96, 0, 120, 121, 0, - 0, 0, 79, 38, 83, 0, 40, 47, 49, 54, - 0, 0, 99, 91, 0, 0, 84, 101, 0, 0, - 85, 97, 0, 92 + 2, 3, 1, 0, 0, 29, 0, 0, 0, 0, + 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 4, 0, 11, 30, 14, 0, + 0, 112, 72, 74, 77, 73, 75, 76, 106, 113, + 0, 0, 0, 15, 36, 58, 59, 90, 91, 102, + 92, 0, 16, 67, 34, 68, 17, 0, 18, 0, + 0, 106, 106, 0, 22, 44, 60, 64, 66, 65, + 61, 92, 20, 0, 30, 46, 47, 23, 106, 0, + 0, 19, 38, 0, 0, 21, 0, 24, 0, 25, + 0, 26, 53, 27, 0, 28, 0, 7, 0, 5, + 0, 10, 115, 114, 0, 0, 0, 0, 35, 0, + 0, 118, 0, 116, 0, 0, 0, 81, 80, 0, + 79, 78, 33, 0, 0, 62, 63, 45, 70, 71, + 0, 43, 0, 0, 70, 37, 0, 0, 0, 0, + 0, 52, 0, 0, 0, 12, 0, 13, 106, 107, + 108, 0, 0, 97, 98, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 0, 0, 0, 0, + 84, 0, 0, 31, 32, 0, 0, 39, 0, 41, + 0, 48, 50, 54, 55, 0, 0, 8, 6, 0, + 111, 109, 110, 0, 0, 0, 128, 127, 126, 0, + 0, 119, 120, 121, 122, 123, 0, 0, 93, 99, + 94, 0, 82, 69, 0, 0, 86, 85, 0, 0, + 0, 0, 0, 0, 104, 100, 0, 124, 125, 0, + 0, 0, 83, 40, 87, 0, 42, 49, 51, 56, + 57, 0, 0, 103, 95, 0, 0, 88, 105, 0, + 0, 89, 101, 0, 96 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 3, 23, 139, 137, 24, 27, 54, 56, - 50, 41, 79, 70, 82, 62, 75, 84, 86, 88, - 90, 51, 63, 52, 64, 43, 53, 171, 208, 44, - 45, 46, 47, 102, 184, 48, 107 + -1, 1, 3, 24, 146, 144, 25, 28, 56, 58, + 52, 43, 81, 72, 85, 64, 77, 87, 89, 91, + 93, 95, 53, 65, 54, 66, 45, 55, 179, 217, + 46, 47, 48, 49, 107, 193, 50, 112 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -107 +#define YYPACT_NINF -96 static const yytype_int16 yypact[] = { - -107, 9, -107, 151, -28, -19, 235, 255, 255, 302, - 175, 6, 282, 34, 363, 255, 255, 255, 363, -3, - -6, 5, -107, -107, 4, -107, -107, -107, 373, 373, - -107, -107, -107, -107, -107, -107, 128, -107, 302, 343, - 373, -107, -107, -107, -107, -107, -107, 17, 19, 115, - -107, -107, 15, -107, -107, 27, -107, 42, 302, 128, - 72, 208, -107, -107, -107, -107, -107, -107, -107, 52, - -107, 44, 302, -107, -107, -107, 72, 359, 373, -107, - -107, 55, -107, 71, -107, 76, -107, 81, -107, 84, - -107, 97, -107, 373, -107, 373, -107, -107, -107, 142, - 373, 373, 114, -107, 1, 116, -107, 102, -107, 129, - 66, 385, -107, -107, 387, -107, -107, -107, 302, 255, - -107, -107, -107, 114, -107, 329, -107, 168, 373, -107, - -107, 149, 18, 302, 302, 302, 397, 151, 447, 151, - 447, 72, -107, -107, 47, 373, 134, -107, 373, 373, - 373, 176, 179, 373, 373, 373, 373, 373, -107, 182, - 3, 148, 152, -107, 401, 153, -107, -107, 158, 166, - 14, -107, 167, 154, 189, -107, -107, 187, -107, -107, - 188, -107, -107, -107, 186, 190, 202, 456, 464, 471, - 373, 373, 146, 146, -107, -107, -107, 373, 373, 192, - -107, -107, 203, -107, -107, 191, 223, 242, -107, 205, - 222, 224, 191, 228, -107, -107, 249, 216, 216, 214, - 215, 233, -107, -107, 261, 244, -107, -107, -107, -107, - 230, 373, -107, -107, 264, 250, -107, -107, 232, 373, - -107, -107, 238, -107 + -96, 34, -96, 158, -38, -30, 267, 288, 288, 338, + 195, 20, 317, 210, 428, 288, 288, 288, 428, 68, + -6, 2, 42, -96, -96, 45, -96, -96, -96, 449, + 449, -96, -96, -96, -96, -96, -96, -96, 127, -96, + 338, 388, 449, -96, -96, -96, -96, -96, -96, 46, + 47, 382, -96, -96, 52, -96, -96, 59, -96, 62, + 338, 127, 19, 238, -96, -96, -96, -96, -96, -96, + -96, 63, -96, 103, 338, -96, -96, -96, 19, 403, + 449, -96, -96, 69, 78, -96, 81, -96, 94, -96, + 97, -96, 98, -96, 102, -96, 111, -96, 449, -96, + 449, -96, -96, -96, 142, 449, 449, 113, -96, 23, + 110, -96, 74, -96, 137, 58, 432, -96, -96, 439, + -96, -96, -96, 338, 288, -96, -96, -96, 113, -96, + 367, -96, 13, 449, -96, -96, 403, 156, 16, 338, + 338, 338, 443, 338, 158, 523, 158, 523, 19, -96, + -96, 11, 449, 140, -96, 449, 449, 449, 182, 194, + 449, 449, 449, 449, 449, -96, 197, 25, 160, 161, + -96, 475, 162, -96, -96, 163, 168, -96, 8, -96, + 169, 173, 174, -96, -96, 175, 176, -96, -96, 177, + -96, -96, -96, 172, 178, 192, 532, 157, 498, 449, + 449, 64, 64, -96, -96, -96, 449, 449, 186, -96, + -96, 191, -96, -96, 200, 215, 244, -96, 205, 222, + 224, 200, 449, 230, -96, -96, 252, 183, 183, 216, + 217, 234, -96, -96, 254, 240, -96, -96, -96, -96, + -96, 221, 449, -96, -96, 271, 255, -96, -96, 236, + 449, -96, -96, 241, -96 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -107, -107, -107, -106, -107, -107, -107, 269, -107, -107, - -107, 273, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -2, 236, 0, -1, -8, -9, 85, -107, 10, - -4, -5, 11, -39, -107, -10, -61 + -96, -96, -96, -95, -96, -96, -96, 270, -96, -96, + -96, 274, -96, -96, -96, -96, -96, -96, -96, -96, + -96, -96, -2, 227, 6, -12, -1, -8, 73, -96, + 12, 1, 5, -3, -49, -96, -10, -44 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If zero, do what YYDEFACT says. - If YYTABLE_NINF, syntax error. */ + number is the opposite. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -1 static const yytype_uint8 yytable[] = { - 69, 66, 65, 81, 42, 68, 67, 57, 55, 2, - 42, 145, 80, 198, 25, 85, 87, 89, 97, 98, - 123, 124, 71, 206, 83, 26, 28, 29, 91, 106, - 108, 178, 138, 179, 140, 94, 103, 129, 92, 116, - 93, 207, 28, 29, 36, 30, 96, 146, 95, 199, - 120, 69, 66, 65, 121, 128, 68, 67, 37, 117, - 115, 30, 109, 78, 110, 170, 40, 81, 106, 122, - 103, 118, 76, 60, 37, 181, 130, 182, 77, 78, - 100, 101, 40, 106, 183, 106, 119, 187, 188, 189, - 142, 143, 192, 193, 194, 195, 196, 127, 160, 161, - 131, 98, 180, 162, 106, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 132, 166, 120, 169, 167, - 133, 121, 172, 28, 111, 134, 177, 176, 135, 217, - 218, 173, 174, 175, 99, 185, 100, 101, 106, 106, - 106, 136, 30, 106, 106, 106, 106, 106, 158, 141, - 112, 113, 4, 36, 98, 37, 155, 156, 157, 144, - 114, 159, 147, 40, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 168, 186, - 106, 106, 190, 28, 29, 58, 191, 219, 220, 19, - 20, 21, 197, 22, 200, 210, 223, 168, 201, 203, - 160, 161, 30, 229, 204, 162, 31, 32, 33, 34, - 205, 209, 35, 59, 60, 37, 28, 29, 125, 61, - 39, 238, 49, 40, 153, 154, 155, 156, 157, 242, - 211, 212, 214, 213, 216, 30, 215, 221, 49, 31, - 32, 33, 34, 28, 29, 35, 59, 60, 37, 222, - 224, 225, 170, 39, 227, 49, 40, 228, 230, 231, - 232, 233, 30, 28, 29, 234, 31, 32, 33, 34, - 235, 236, 35, 36, 239, 37, 237, 240, 241, 38, - 39, 73, 30, 40, 243, 74, 31, 32, 33, 34, - 28, 29, 35, 36, 226, 37, 0, 126, 0, 0, - 39, 0, 49, 40, 0, 0, 0, 0, 0, 30, - 28, 29, 0, 31, 32, 33, 34, 0, 0, 35, - 36, 0, 37, 0, 0, 0, 72, 39, 0, 30, - 40, 0, 0, 31, 32, 33, 34, 28, 29, 35, - 36, 0, 37, 0, 0, 0, 0, 39, 0, 0, - 40, 28, 29, 0, 0, 0, 30, 0, 0, 0, - 31, 32, 33, 34, 0, 0, 35, 28, 29, 37, - 30, 28, 29, 0, 39, 104, 0, 40, 0, 0, - 105, 28, 29, 37, 0, 0, 30, 0, 78, 0, - 30, 40, 0, 28, 29, 28, 164, 76, 60, 37, - 30, 36, 0, 37, 78, 28, 29, 40, 39, 28, - 29, 40, 30, 37, 30, 0, 0, 0, 78, 0, - 163, 40, 165, 0, 30, 37, 0, 37, 30, 0, - 78, 0, 78, 40, 0, 40, 202, 37, 0, 0, - 0, 37, 78, 0, 49, 40, 78, 0, 0, 40, - 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, - 149, 150, 151, 152, 153, 154, 155, 156, 157, 150, - 151, 152, 153, 154, 155, 156, 157, 151, 152, 153, - 154, 155, 156, 157 + 71, 82, 68, 84, 44, 83, 26, 59, 73, 67, + 44, 69, 128, 129, 57, 70, 27, 215, 96, 102, + 103, 88, 90, 92, 29, 30, 86, 105, 106, 134, + 94, 111, 113, 152, 2, 207, 216, 97, 108, 98, + 190, 121, 191, 175, 31, 99, 167, 168, 120, 187, + 192, 188, 169, 71, 145, 68, 147, 127, 39, 125, + 38, 126, 67, 80, 69, 178, 42, 135, 70, 84, + 111, 153, 108, 208, 162, 163, 164, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 100, 111, 101, + 111, 167, 168, 114, 115, 149, 150, 169, 122, 189, + 32, 33, 34, 35, 36, 123, 103, 37, 124, 111, + 132, 196, 197, 198, 133, 136, 201, 202, 203, 204, + 205, 173, 165, 176, 177, 137, 84, 138, 180, 125, + 174, 126, 185, 104, 184, 105, 106, 181, 182, 183, + 139, 186, 194, 140, 141, 111, 111, 111, 142, 148, + 111, 111, 111, 111, 111, 227, 228, 143, 154, 4, + 151, 103, 157, 158, 159, 160, 161, 162, 163, 164, + 166, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 175, 195, 199, 111, + 111, 160, 161, 162, 163, 164, 229, 230, 20, 21, + 22, 200, 23, 29, 30, 60, 233, 206, 209, 210, + 212, 213, 240, 239, 214, 218, 219, 220, 29, 30, + 224, 221, 222, 31, 223, 226, 225, 32, 33, 34, + 35, 36, 249, 231, 37, 61, 62, 39, 31, 232, + 253, 63, 41, 234, 51, 42, 29, 30, 130, 51, + 78, 62, 39, 235, 178, 237, 79, 80, 238, 51, + 42, 241, 242, 246, 243, 244, 31, 245, 247, 248, + 32, 33, 34, 35, 36, 29, 30, 37, 61, 62, + 39, 250, 75, 251, 252, 41, 76, 51, 42, 254, + 131, 236, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 38, 0, 39, + 0, 0, 0, 40, 41, 0, 31, 42, 0, 0, + 32, 33, 34, 35, 36, 29, 30, 37, 38, 0, + 39, 0, 0, 0, 0, 41, 0, 51, 42, 0, + 0, 0, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 38, 0, 39, + 0, 0, 0, 74, 41, 0, 31, 42, 0, 0, + 32, 33, 34, 35, 36, 29, 30, 37, 38, 0, + 39, 0, 0, 0, 0, 41, 0, 0, 42, 0, + 29, 116, 0, 0, 0, 31, 29, 30, 0, 32, + 33, 34, 35, 36, 0, 0, 37, 0, 0, 39, + 31, 29, 30, 0, 41, 0, 31, 42, 0, 117, + 118, 109, 38, 0, 39, 0, 0, 110, 0, 119, + 39, 31, 42, 0, 0, 80, 29, 30, 42, 0, + 29, 30, 0, 78, 62, 39, 0, 29, 171, 0, + 80, 29, 30, 42, 0, 0, 31, 29, 30, 0, + 31, 0, 0, 0, 0, 0, 0, 31, 38, 170, + 39, 31, 0, 0, 39, 41, 172, 31, 42, 80, + 0, 39, 42, 29, 30, 39, 80, 0, 0, 42, + 80, 39, 51, 42, 0, 0, 80, 0, 0, 42, + 0, 0, 0, 31, 158, 159, 160, 161, 162, 163, + 164, 0, 211, 0, 0, 0, 0, 39, 0, 0, + 0, 0, 80, 0, 0, 42, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 156, 157, 158, 159, + 160, 161, 162, 163, 164 }; +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-96))) + +#define yytable_value_is_error(Yytable_value) \ + YYID (0) + static const yytype_int16 yycheck[] = { - 10, 10, 10, 13, 6, 10, 10, 9, 8, 0, - 12, 10, 13, 10, 42, 15, 16, 17, 28, 29, - 59, 60, 11, 9, 14, 44, 8, 9, 18, 39, - 40, 137, 93, 139, 95, 41, 38, 76, 41, 49, - 43, 27, 8, 9, 38, 27, 42, 46, 43, 46, - 58, 61, 61, 61, 58, 11, 61, 61, 40, 44, - 49, 27, 45, 45, 45, 47, 48, 77, 78, 58, - 72, 44, 38, 39, 40, 28, 77, 30, 44, 45, - 8, 9, 48, 93, 37, 95, 44, 148, 149, 150, - 100, 101, 153, 154, 155, 156, 157, 45, 32, 33, - 45, 111, 141, 37, 114, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 44, 118, 125, 128, 119, - 44, 125, 132, 8, 9, 44, 136, 136, 44, 190, - 191, 133, 134, 135, 6, 145, 8, 9, 148, 149, - 150, 44, 27, 153, 154, 155, 156, 157, 46, 7, - 35, 36, 1, 38, 164, 40, 10, 11, 12, 45, - 45, 32, 46, 48, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 29, 45, - 190, 191, 6, 8, 9, 10, 7, 197, 198, 38, - 39, 40, 10, 42, 46, 41, 205, 29, 46, 46, - 32, 33, 27, 212, 46, 37, 31, 32, 33, 34, - 44, 44, 37, 38, 39, 40, 8, 9, 10, 44, - 45, 231, 47, 48, 8, 9, 10, 11, 12, 239, - 41, 44, 46, 45, 32, 27, 46, 45, 47, 31, - 32, 33, 34, 8, 9, 37, 38, 39, 40, 46, - 27, 9, 47, 45, 32, 47, 48, 33, 30, 10, - 46, 46, 27, 8, 9, 32, 31, 32, 33, 34, - 9, 27, 37, 38, 10, 40, 46, 27, 46, 44, - 45, 12, 27, 48, 46, 12, 31, 32, 33, 34, - 8, 9, 37, 38, 209, 40, -1, 61, -1, -1, - 45, -1, 47, 48, -1, -1, -1, -1, -1, 27, - 8, 9, -1, 31, 32, 33, 34, -1, -1, 37, - 38, -1, 40, -1, -1, -1, 44, 45, -1, 27, - 48, -1, -1, 31, 32, 33, 34, 8, 9, 37, - 38, -1, 40, -1, -1, -1, -1, 45, -1, -1, - 48, 8, 9, -1, -1, -1, 27, -1, -1, -1, - 31, 32, 33, 34, -1, -1, 37, 8, 9, 40, - 27, 8, 9, -1, 45, 32, -1, 48, -1, -1, - 37, 8, 9, 40, -1, -1, 27, -1, 45, -1, - 27, 48, -1, 8, 9, 8, 9, 38, 39, 40, - 27, 38, -1, 40, 45, 8, 9, 48, 45, 8, - 9, 48, 27, 40, 27, -1, -1, -1, 45, -1, - 35, 48, 35, -1, 27, 40, -1, 40, 27, -1, - 45, -1, 45, 48, -1, 48, 35, 40, -1, -1, - -1, 40, 45, -1, 47, 48, 45, -1, -1, 48, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 5, - 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, - 9, 10, 11, 12 + 10, 13, 10, 13, 6, 13, 44, 9, 11, 10, + 12, 10, 61, 62, 8, 10, 46, 9, 19, 29, + 30, 15, 16, 17, 8, 9, 14, 8, 9, 78, + 18, 41, 42, 10, 0, 10, 28, 43, 40, 45, + 29, 51, 31, 30, 28, 43, 33, 34, 51, 144, + 39, 146, 39, 63, 98, 63, 100, 60, 42, 60, + 40, 60, 63, 47, 63, 49, 50, 79, 63, 79, + 80, 48, 74, 48, 10, 11, 12, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 45, 98, 44, + 100, 33, 34, 47, 47, 105, 106, 39, 46, 148, + 32, 33, 34, 35, 36, 46, 116, 39, 46, 119, + 47, 155, 156, 157, 11, 46, 160, 161, 162, 163, + 164, 123, 48, 133, 136, 47, 136, 46, 138, 130, + 124, 130, 142, 6, 142, 8, 9, 139, 140, 141, + 46, 143, 152, 46, 46, 155, 156, 157, 46, 7, + 160, 161, 162, 163, 164, 199, 200, 46, 48, 1, + 47, 171, 5, 6, 7, 8, 9, 10, 11, 12, + 33, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 30, 47, 6, 199, + 200, 8, 9, 10, 11, 12, 206, 207, 40, 41, + 42, 7, 44, 8, 9, 10, 214, 10, 48, 48, + 48, 48, 222, 221, 46, 46, 43, 43, 8, 9, + 48, 46, 46, 28, 47, 33, 48, 32, 33, 34, + 35, 36, 242, 47, 39, 40, 41, 42, 28, 48, + 250, 46, 47, 28, 49, 50, 8, 9, 10, 49, + 40, 41, 42, 9, 49, 33, 46, 47, 34, 49, + 50, 31, 10, 9, 48, 48, 28, 33, 28, 48, + 32, 33, 34, 35, 36, 8, 9, 39, 40, 41, + 42, 10, 12, 28, 48, 47, 12, 49, 50, 48, + 63, 218, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, 40, -1, 42, + -1, -1, -1, 46, 47, -1, 28, 50, -1, -1, + 32, 33, 34, 35, 36, 8, 9, 39, 40, -1, + 42, -1, -1, -1, -1, 47, -1, 49, 50, -1, + -1, -1, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, 40, -1, 42, + -1, -1, -1, 46, 47, -1, 28, 50, -1, -1, + 32, 33, 34, 35, 36, 8, 9, 39, 40, -1, + 42, -1, -1, -1, -1, 47, -1, -1, 50, -1, + 8, 9, -1, -1, -1, 28, 8, 9, -1, 32, + 33, 34, 35, 36, -1, -1, 39, -1, -1, 42, + 28, 8, 9, -1, 47, -1, 28, 50, -1, 37, + 38, 33, 40, -1, 42, -1, -1, 39, -1, 47, + 42, 28, 50, -1, -1, 47, 8, 9, 50, -1, + 8, 9, -1, 40, 41, 42, -1, 8, 9, -1, + 47, 8, 9, 50, -1, -1, 28, 8, 9, -1, + 28, -1, -1, -1, -1, -1, -1, 28, 40, 37, + 42, 28, -1, -1, 42, 47, 37, 28, 50, 47, + -1, 42, 50, 8, 9, 42, 47, -1, -1, 50, + 47, 42, 49, 50, -1, -1, 47, -1, -1, 50, + -1, -1, -1, 28, 6, 7, 8, 9, 10, 11, + 12, -1, 37, -1, -1, -1, -1, 42, -1, -1, + -1, -1, 47, -1, -1, 50, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 4, 5, 6, 7, + 8, 9, 10, 11, 12 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 50, 0, 51, 1, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 38, - 39, 40, 42, 52, 55, 42, 44, 56, 8, 9, - 27, 31, 32, 33, 34, 37, 38, 40, 44, 45, - 48, 60, 70, 74, 78, 79, 80, 81, 84, 47, - 59, 70, 72, 75, 57, 72, 58, 70, 10, 38, - 39, 44, 64, 71, 73, 74, 75, 79, 80, 84, - 62, 81, 44, 56, 60, 65, 38, 44, 45, 61, - 73, 84, 63, 78, 66, 72, 67, 72, 68, 72, - 69, 78, 41, 43, 41, 43, 42, 84, 84, 6, - 8, 9, 82, 70, 32, 37, 84, 85, 84, 45, - 45, 9, 35, 36, 45, 81, 84, 44, 44, 44, - 74, 79, 81, 82, 82, 10, 71, 45, 11, 82, - 73, 45, 44, 44, 44, 44, 44, 54, 85, 53, - 85, 7, 84, 84, 45, 10, 46, 46, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 46, 32, - 32, 33, 37, 35, 9, 35, 70, 72, 29, 84, - 47, 76, 84, 70, 70, 70, 75, 84, 52, 52, - 82, 28, 30, 37, 83, 84, 45, 85, 85, 85, - 6, 7, 85, 85, 85, 85, 85, 10, 10, 46, - 46, 46, 35, 46, 46, 44, 9, 27, 77, 44, - 41, 41, 44, 45, 46, 46, 32, 85, 85, 84, - 84, 45, 46, 75, 27, 9, 76, 32, 33, 75, - 30, 10, 46, 46, 32, 9, 27, 46, 84, 10, - 27, 46, 84, 46 + 0, 52, 0, 53, 1, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 40, 41, 42, 44, 54, 57, 44, 46, 58, 8, + 9, 28, 32, 33, 34, 35, 36, 39, 40, 42, + 46, 47, 50, 62, 73, 77, 81, 82, 83, 84, + 87, 49, 61, 73, 75, 78, 59, 75, 60, 73, + 10, 40, 41, 46, 66, 74, 76, 77, 78, 82, + 83, 87, 64, 84, 46, 58, 62, 67, 40, 46, + 47, 63, 76, 78, 87, 65, 81, 68, 75, 69, + 75, 70, 75, 71, 81, 72, 77, 43, 45, 43, + 45, 44, 87, 87, 6, 8, 9, 85, 73, 33, + 39, 87, 88, 87, 47, 47, 9, 37, 38, 47, + 84, 87, 46, 46, 46, 77, 82, 84, 85, 85, + 10, 74, 47, 11, 85, 76, 46, 47, 46, 46, + 46, 46, 46, 46, 56, 88, 55, 88, 7, 87, + 87, 47, 10, 48, 48, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 48, 33, 33, 34, 39, + 37, 9, 37, 73, 75, 30, 87, 76, 49, 79, + 87, 73, 73, 73, 78, 87, 73, 54, 54, 85, + 29, 31, 39, 86, 87, 47, 88, 88, 88, 6, + 7, 88, 88, 88, 88, 88, 10, 10, 48, 48, + 48, 37, 48, 48, 46, 9, 28, 80, 46, 43, + 43, 46, 46, 47, 48, 48, 33, 88, 88, 87, + 87, 47, 48, 78, 28, 9, 79, 33, 34, 78, + 87, 31, 10, 48, 48, 33, 9, 28, 48, 87, + 10, 28, 48, 87, 48 }; #define yyerrok (yyerrstatus = 0) @@ -857,78 +905,50 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. */ + Once GCC version 2 has supplanted version 1, this can go. However, + YYFAIL appears to be in use. Nevertheless, it is formally deprecated + in Bison 2.4.2's NEWS entry, where a plan to phase it out is + discussed. */ #define YYFAIL goto yyerrlab +#if defined YYFAIL + /* This is here to suppress warnings from the GCC cpp's + -Wunused-macros. Normally we don't worry about that warning, but + some users do, and we want to make it easy for users to remove + YYFAIL uses, which will produce warnings from Bison 2.5. */ +#endif #define YYRECOVERING() (!!yyerrstatus) -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY && yylen == 1) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - yytoken = YYTRANSLATE (yychar); \ - YYPOPSTACK (1); \ - goto yybackup; \ - } \ - else \ - { \ +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) - +/* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 -/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. - If N is 0, then set CURRENT to the empty location which ends - the previous symbol: RHS[0] (always defined). */ - -#define YYRHSLOC(Rhs, K) ((Rhs)[K]) -#ifndef YYLLOC_DEFAULT -# define YYLLOC_DEFAULT(Current, Rhs, N) \ - do \ - if (YYID (N)) \ - { \ - (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC (Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ - } \ - while (YYID (0)) -#endif - - -/* YY_LOCATION_PRINT -- Print the location on the stream. - This macro was not mandated originally: define only if we know - we won't break user code: when these are the locations we know. */ - +/* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT -# if YYLTYPE_IS_TRIVIAL -# define YY_LOCATION_PRINT(File, Loc) \ - fprintf (File, "%d.%d-%d.%d", \ - (Loc).first_line, (Loc).first_column, \ - (Loc).last_line, (Loc).last_column) -# else -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -# endif +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif /* YYLEX -- calling `yylex' with the right arguments. */ - #ifdef YYLEX_PARAM # define YYLEX yylex (YYLEX_PARAM) #else @@ -978,6 +998,8 @@ yy_symbol_value_print (yyoutput, yytype, yyvaluep) YYSTYPE const * const yyvaluep; #endif { + FILE *yyo = yyoutput; + YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT @@ -1115,7 +1137,6 @@ int yydebug; # define YYMAXDEPTH 10000 #endif - #if YYERROR_VERBOSE @@ -1218,115 +1239,142 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into YYRESULT an error message about the unexpected token - YYCHAR while in state YYSTATE. Return the number of bytes copied, - including the terminating null byte. If YYRESULT is null, do not - copy anything; just return the number of bytes that would be - copied. As a special case, return 0 if an ordinary "syntax error" - message will do. Return YYSIZE_MAXIMUM if overflow occurs during - size calculation. */ -static YYSIZE_T -yysyntax_error (char *yyresult, int yystate, int yychar) -{ - int yyn = yypact[yystate]; +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. - if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) - return 0; - else - { - int yytype = YYTRANSLATE (yychar); - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - int yysize_overflow = 0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - int yyx; - -# if 0 - /* This is so xgettext sees the translatable formats that are - constructed on the fly. */ - YY_("syntax error, unexpected %s"); - YY_("syntax error, unexpected %s, expecting %s"); - YY_("syntax error, unexpected %s, expecting %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s"); - YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); -# endif - char *yyfmt; - char const *yyf; - static char const yyunexpected[] = "syntax error, unexpected %s"; - static char const yyexpecting[] = ", expecting %s"; - static char const yyor[] = " or %s"; - char yyformat[sizeof yyunexpected - + sizeof yyexpecting - 1 - + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) - * (sizeof yyor - 1))]; - char const *yyprefix = yyexpecting; - - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 1; - - yyarg[0] = yytname[yytype]; - yyfmt = yystpcpy (yyformat, yyunexpected); - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - yyformat[sizeof yyunexpected - 1] = '\0'; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; - yyfmt = yystpcpy (yyfmt, yyprefix); - yyprefix = yyor; - } + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULL, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULL; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - Assume YYFAIL is not used. It's too flawed to consider. See + <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> + for details. YYERROR is fine as it does not invoke this + function. + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (YY_NULL, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } - yyf = YY_(yyformat); - yysize1 = yysize + yystrlen (yyf); - yysize_overflow |= (yysize1 < yysize); - yysize = yysize1; + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } - if (yysize_overflow) - return YYSIZE_MAXIMUM; + yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; - if (yyresult) - { - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - char *yyp = yyresult; - int yyi = 0; - while ((*yyp = *yyf) != '\0') - { - if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyf += 2; - } - else - { - yyp++; - yyf++; - } - } - } - return yysize; + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; } #endif /* YYERROR_VERBOSE */ - /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -1359,36 +1407,31 @@ yydestruct (yymsg, yytype, yyvaluep) } } -/* Prevent warnings from -Wmissing-prototypes. */ -#ifdef YYPARSE_PARAM -#if defined __STDC__ || defined __cplusplus -int yyparse (void *YYPARSE_PARAM); -#else -int yyparse (); -#endif -#else /* ! YYPARSE_PARAM */ -#if defined __STDC__ || defined __cplusplus -int yyparse (void); -#else -int yyparse (); -#endif -#endif /* ! YYPARSE_PARAM */ + /* The lookahead symbol. */ int yychar; + +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + /* The semantic value of the lookahead symbol. */ -YYSTYPE yylval; +YYSTYPE yylval YY_INITIAL_VALUE(yyval_default); /* Number of syntax errors so far. */ int yynerrs; - -/*-------------------------. -| yyparse or yypush_parse. | -`-------------------------*/ +/*----------. +| yyparse. | +`----------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ @@ -1412,8 +1455,6 @@ yyparse () #endif #endif { - - int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; @@ -1422,7 +1463,7 @@ yyparse () `yyss': related to states. `yyvs': related to semantic values. - Refer to the stacks thru separate pointers, to allow yyoverflow + Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ @@ -1440,7 +1481,7 @@ yyparse () int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ - int yytoken; + int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; @@ -1458,9 +1499,8 @@ yyparse () Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); @@ -1469,14 +1509,6 @@ yyparse () yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ - - /* Initialize stack pointers. - Waste one element of value and location stack - so that they stay on the same level as the state stack. - The wasted elements are never initialized. */ - yyssp = yyss; - yyvsp = yyvs; - goto yysetstate; /*------------------------------------------------------------. @@ -1568,7 +1600,7 @@ yybackup: /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; - if (yyn == YYPACT_NINF) + if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ @@ -1599,8 +1631,8 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yyn == 0 || yyn == YYTABLE_NINF) - goto yyerrlab; + if (yytable_value_is_error (yyn)) + goto yyerrlab; yyn = -yyn; goto yyreduce; } @@ -1617,7 +1649,9 @@ yybackup: yychar = YYEMPTY; yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; @@ -1654,8 +1688,7 @@ yyreduce: switch (yyn) { case 3: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 70 "a.y" { stmtline = lineno; @@ -1663,8 +1696,7 @@ yyreduce: break; case 5: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 77 "a.y" { if((yyvsp[(1) - (2)].sym)->value != pc) @@ -1674,8 +1706,7 @@ yyreduce: break; case 7: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 84 "a.y" { (yyvsp[(1) - (2)].sym)->type = LLAB; @@ -1684,8 +1715,7 @@ yyreduce: break; case 12: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 95 "a.y" { (yyvsp[(1) - (3)].sym)->type = LVAR; @@ -1694,8 +1724,7 @@ yyreduce: break; case 13: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 100 "a.y" { if((yyvsp[(1) - (3)].sym)->value != (yyvsp[(3) - (3)].lval)) @@ -1705,207 +1734,197 @@ yyreduce: break; case 14: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 105 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 15: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 106 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 16: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 107 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 17: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 108 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 18: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 109 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 19: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 110 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 20: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 111 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 21: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 112 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 22: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 113 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 23: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 114 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 24: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 115 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 25: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 116 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 26: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 117 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 27: - -/* Line 1455 of yacc.c */ +/* Line 1778 of yacc.c */ #line 118 "a.y" { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } break; case 28: +/* Line 1778 of yacc.c */ +#line 119 "a.y" + { outcode((yyvsp[(1) - (2)].lval), &(yyvsp[(2) - (2)].gen2)); } + break; -/* Line 1455 of yacc.c */ -#line 121 "a.y" + case 29: +/* Line 1778 of yacc.c */ +#line 122 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 29: - -/* Line 1455 of yacc.c */ -#line 126 "a.y" + case 30: +/* Line 1778 of yacc.c */ +#line 127 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = nullgen; } break; - case 30: - -/* Line 1455 of yacc.c */ -#line 133 "a.y" + case 31: +/* Line 1778 of yacc.c */ +#line 134 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 31: - -/* Line 1455 of yacc.c */ -#line 140 "a.y" + case 32: +/* Line 1778 of yacc.c */ +#line 141 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 32: - -/* Line 1455 of yacc.c */ -#line 147 "a.y" + case 33: +/* Line 1778 of yacc.c */ +#line 148 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 33: - -/* Line 1455 of yacc.c */ -#line 152 "a.y" + case 34: +/* Line 1778 of yacc.c */ +#line 153 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 34: - -/* Line 1455 of yacc.c */ -#line 159 "a.y" + case 35: +/* Line 1778 of yacc.c */ +#line 160 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 35: - -/* Line 1455 of yacc.c */ -#line 164 "a.y" + case 36: +/* Line 1778 of yacc.c */ +#line 165 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 36: - -/* Line 1455 of yacc.c */ -#line 171 "a.y" + case 37: +/* Line 1778 of yacc.c */ +#line 172 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 37: - -/* Line 1455 of yacc.c */ -#line 176 "a.y" + case 38: +/* Line 1778 of yacc.c */ +#line 177 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 38: + case 39: +/* Line 1778 of yacc.c */ +#line 182 "a.y" + { + (yyval.gen2).from = (yyvsp[(1) - (3)].gen); + (yyval.gen2).to = (yyvsp[(3) - (3)].gen); + } + break; -/* Line 1455 of yacc.c */ -#line 183 "a.y" + case 40: +/* Line 1778 of yacc.c */ +#line 189 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1913,20 +1932,18 @@ yyreduce: } break; - case 39: - -/* Line 1455 of yacc.c */ -#line 191 "a.y" + case 41: +/* Line 1778 of yacc.c */ +#line 197 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 40: - -/* Line 1455 of yacc.c */ -#line 196 "a.y" + case 42: +/* Line 1778 of yacc.c */ +#line 202 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -1934,30 +1951,27 @@ yyreduce: } break; - case 41: - -/* Line 1455 of yacc.c */ -#line 204 "a.y" + case 43: +/* Line 1778 of yacc.c */ +#line 210 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); } break; - case 42: - -/* Line 1455 of yacc.c */ -#line 209 "a.y" + case 44: +/* Line 1778 of yacc.c */ +#line 215 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(1) - (1)].gen); } break; - case 43: - -/* Line 1455 of yacc.c */ -#line 214 "a.y" + case 45: +/* Line 1778 of yacc.c */ +#line 220 "a.y" { (yyval.gen2).from = nullgen; (yyval.gen2).to = (yyvsp[(2) - (2)].gen); @@ -1966,20 +1980,18 @@ yyreduce: } break; - case 46: - -/* Line 1455 of yacc.c */ -#line 227 "a.y" + case 48: +/* Line 1778 of yacc.c */ +#line 233 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 47: - -/* Line 1455 of yacc.c */ -#line 232 "a.y" + case 49: +/* Line 1778 of yacc.c */ +#line 238 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -1989,20 +2001,18 @@ yyreduce: } break; - case 48: - -/* Line 1455 of yacc.c */ -#line 242 "a.y" + case 50: +/* Line 1778 of yacc.c */ +#line 248 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 49: - -/* Line 1455 of yacc.c */ -#line 247 "a.y" + case 51: +/* Line 1778 of yacc.c */ +#line 253 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).to = (yyvsp[(3) - (5)].gen); @@ -2012,50 +2022,45 @@ yyreduce: } break; - case 50: - -/* Line 1455 of yacc.c */ -#line 257 "a.y" + case 52: +/* Line 1778 of yacc.c */ +#line 263 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (2)].gen); (yyval.gen2).to = nullgen; } break; - case 51: - -/* Line 1455 of yacc.c */ -#line 262 "a.y" + case 53: +/* Line 1778 of yacc.c */ +#line 268 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (1)].gen); (yyval.gen2).to = nullgen; } break; - case 52: - -/* Line 1455 of yacc.c */ -#line 267 "a.y" + case 54: +/* Line 1778 of yacc.c */ +#line 273 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 53: - -/* Line 1455 of yacc.c */ -#line 274 "a.y" + case 55: +/* Line 1778 of yacc.c */ +#line 280 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (3)].gen); (yyval.gen2).to = (yyvsp[(3) - (3)].gen); } break; - case 54: - -/* Line 1455 of yacc.c */ -#line 279 "a.y" + case 56: +/* Line 1778 of yacc.c */ +#line 285 "a.y" { (yyval.gen2).from = (yyvsp[(1) - (5)].gen); (yyval.gen2).from.scale = (yyvsp[(3) - (5)].lval); @@ -2063,28 +2068,35 @@ yyreduce: } break; - case 59: - -/* Line 1455 of yacc.c */ + case 57: +/* Line 1778 of yacc.c */ #line 293 "a.y" { - (yyval.gen) = (yyvsp[(2) - (2)].gen); + (yyval.gen2).from = (yyvsp[(1) - (5)].gen); + (yyval.gen2).to = (yyvsp[(3) - (5)].gen); + (yyval.gen2).to.offset = (yyvsp[(5) - (5)].lval); } break; - case 60: - -/* Line 1455 of yacc.c */ -#line 297 "a.y" + case 62: +/* Line 1778 of yacc.c */ +#line 307 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); } break; - case 66: + case 63: +/* Line 1778 of yacc.c */ +#line 311 "a.y" + { + (yyval.gen) = (yyvsp[(2) - (2)].gen); + } + break; -/* Line 1455 of yacc.c */ -#line 310 "a.y" + case 69: +/* Line 1778 of yacc.c */ +#line 324 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2092,10 +2104,9 @@ yyreduce: } break; - case 67: - -/* Line 1455 of yacc.c */ -#line 316 "a.y" + case 70: +/* Line 1778 of yacc.c */ +#line 330 "a.y" { (yyval.gen) = nullgen; if(pass == 2) @@ -2106,10 +2117,9 @@ yyreduce: } break; - case 68: - -/* Line 1455 of yacc.c */ -#line 325 "a.y" + case 71: +/* Line 1778 of yacc.c */ +#line 339 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_BRANCH; @@ -2118,60 +2128,63 @@ yyreduce: } break; - case 69: - -/* Line 1455 of yacc.c */ -#line 334 "a.y" + case 72: +/* Line 1778 of yacc.c */ +#line 348 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 70: - -/* Line 1455 of yacc.c */ -#line 339 "a.y" + case 73: +/* Line 1778 of yacc.c */ +#line 353 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 71: - -/* Line 1455 of yacc.c */ -#line 344 "a.y" + case 74: +/* Line 1778 of yacc.c */ +#line 358 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 72: + case 75: +/* Line 1778 of yacc.c */ +#line 363 "a.y" + { + (yyval.gen) = nullgen; + (yyval.gen).type = (yyvsp[(1) - (1)].lval); + } + break; -/* Line 1455 of yacc.c */ -#line 349 "a.y" + case 76: +/* Line 1778 of yacc.c */ +#line 368 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SP; } break; - case 73: - -/* Line 1455 of yacc.c */ -#line 354 "a.y" + case 77: +/* Line 1778 of yacc.c */ +#line 373 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(1) - (1)].lval); } break; - case 74: - -/* Line 1455 of yacc.c */ -#line 361 "a.y" + case 78: +/* Line 1778 of yacc.c */ +#line 380 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST; @@ -2179,10 +2192,9 @@ yyreduce: } break; - case 75: - -/* Line 1455 of yacc.c */ -#line 367 "a.y" + case 79: +/* Line 1778 of yacc.c */ +#line 386 "a.y" { (yyval.gen) = (yyvsp[(2) - (2)].gen); (yyval.gen).index = (yyvsp[(2) - (2)].gen).type; @@ -2195,10 +2207,9 @@ yyreduce: } break; - case 76: - -/* Line 1455 of yacc.c */ -#line 378 "a.y" + case 80: +/* Line 1778 of yacc.c */ +#line 397 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_SCONST; @@ -2206,10 +2217,9 @@ yyreduce: } break; - case 77: - -/* Line 1455 of yacc.c */ -#line 384 "a.y" + case 81: +/* Line 1778 of yacc.c */ +#line 403 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2217,10 +2227,9 @@ yyreduce: } break; - case 78: - -/* Line 1455 of yacc.c */ -#line 390 "a.y" + case 82: +/* Line 1778 of yacc.c */ +#line 409 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2228,10 +2237,9 @@ yyreduce: } break; - case 79: - -/* Line 1455 of yacc.c */ -#line 396 "a.y" + case 83: +/* Line 1778 of yacc.c */ +#line 415 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2239,10 +2247,9 @@ yyreduce: } break; - case 80: - -/* Line 1455 of yacc.c */ -#line 402 "a.y" + case 84: +/* Line 1778 of yacc.c */ +#line 421 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_FCONST; @@ -2250,10 +2257,9 @@ yyreduce: } break; - case 81: - -/* Line 1455 of yacc.c */ -#line 410 "a.y" + case 85: +/* Line 1778 of yacc.c */ +#line 429 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_CONST2; @@ -2262,50 +2268,45 @@ yyreduce: } break; - case 82: - -/* Line 1455 of yacc.c */ -#line 419 "a.y" + case 86: +/* Line 1778 of yacc.c */ +#line 438 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (1)].lval); (yyval.con2).v2 = 0; } break; - case 83: - -/* Line 1455 of yacc.c */ -#line 424 "a.y" + case 87: +/* Line 1778 of yacc.c */ +#line 443 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (2)].lval); (yyval.con2).v2 = 0; } break; - case 84: - -/* Line 1455 of yacc.c */ -#line 429 "a.y" + case 88: +/* Line 1778 of yacc.c */ +#line 448 "a.y" { (yyval.con2).v1 = (yyvsp[(1) - (3)].lval); (yyval.con2).v2 = (yyvsp[(3) - (3)].lval); } break; - case 85: - -/* Line 1455 of yacc.c */ -#line 434 "a.y" + case 89: +/* Line 1778 of yacc.c */ +#line 453 "a.y" { (yyval.con2).v1 = -(yyvsp[(2) - (4)].lval); (yyval.con2).v2 = (yyvsp[(4) - (4)].lval); } break; - case 88: - -/* Line 1455 of yacc.c */ -#line 445 "a.y" + case 92: +/* Line 1778 of yacc.c */ +#line 464 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2313,10 +2314,9 @@ yyreduce: } break; - case 89: - -/* Line 1455 of yacc.c */ -#line 451 "a.y" + case 93: +/* Line 1778 of yacc.c */ +#line 470 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2324,10 +2324,9 @@ yyreduce: } break; - case 90: - -/* Line 1455 of yacc.c */ -#line 457 "a.y" + case 94: +/* Line 1778 of yacc.c */ +#line 476 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; @@ -2335,10 +2334,9 @@ yyreduce: } break; - case 91: - -/* Line 1455 of yacc.c */ -#line 463 "a.y" + case 95: +/* Line 1778 of yacc.c */ +#line 482 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2349,10 +2347,9 @@ yyreduce: } break; - case 92: - -/* Line 1455 of yacc.c */ -#line 472 "a.y" + case 96: +/* Line 1778 of yacc.c */ +#line 491 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (9)].lval); @@ -2363,30 +2360,27 @@ yyreduce: } break; - case 93: - -/* Line 1455 of yacc.c */ -#line 481 "a.y" + case 97: +/* Line 1778 of yacc.c */ +#line 500 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (3)].lval); } break; - case 94: - -/* Line 1455 of yacc.c */ -#line 486 "a.y" + case 98: +/* Line 1778 of yacc.c */ +#line 505 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_SP; } break; - case 95: - -/* Line 1455 of yacc.c */ -#line 491 "a.y" + case 99: +/* Line 1778 of yacc.c */ +#line 510 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(3) - (4)].lval); @@ -2394,10 +2388,9 @@ yyreduce: } break; - case 96: - -/* Line 1455 of yacc.c */ -#line 497 "a.y" + case 100: +/* Line 1778 of yacc.c */ +#line 516 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+D_NONE; @@ -2407,10 +2400,9 @@ yyreduce: } break; - case 97: - -/* Line 1455 of yacc.c */ -#line 505 "a.y" + case 101: +/* Line 1778 of yacc.c */ +#line 524 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_INDIR+(yyvsp[(2) - (8)].lval); @@ -2420,19 +2412,17 @@ yyreduce: } break; - case 98: - -/* Line 1455 of yacc.c */ -#line 515 "a.y" + case 102: +/* Line 1778 of yacc.c */ +#line 534 "a.y" { (yyval.gen) = (yyvsp[(1) - (1)].gen); } break; - case 99: - -/* Line 1455 of yacc.c */ -#line 519 "a.y" + case 103: +/* Line 1778 of yacc.c */ +#line 538 "a.y" { (yyval.gen) = (yyvsp[(1) - (6)].gen); (yyval.gen).index = (yyvsp[(3) - (6)].lval); @@ -2441,10 +2431,9 @@ yyreduce: } break; - case 100: - -/* Line 1455 of yacc.c */ -#line 528 "a.y" + case 104: +/* Line 1778 of yacc.c */ +#line 547 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = (yyvsp[(4) - (5)].lval); @@ -2453,10 +2442,9 @@ yyreduce: } break; - case 101: - -/* Line 1455 of yacc.c */ -#line 535 "a.y" + case 105: +/* Line 1778 of yacc.c */ +#line 554 "a.y" { (yyval.gen) = nullgen; (yyval.gen).type = D_STATIC; @@ -2465,183 +2453,174 @@ yyreduce: } break; - case 102: - -/* Line 1455 of yacc.c */ -#line 543 "a.y" + case 106: +/* Line 1778 of yacc.c */ +#line 562 "a.y" { (yyval.lval) = 0; } break; - case 103: - -/* Line 1455 of yacc.c */ -#line 547 "a.y" + case 107: +/* Line 1778 of yacc.c */ +#line 566 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 104: - -/* Line 1455 of yacc.c */ -#line 551 "a.y" + case 108: +/* Line 1778 of yacc.c */ +#line 570 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 106: - -/* Line 1455 of yacc.c */ -#line 558 "a.y" + case 110: +/* Line 1778 of yacc.c */ +#line 577 "a.y" { (yyval.lval) = D_AUTO; } break; - case 109: - -/* Line 1455 of yacc.c */ -#line 566 "a.y" + case 113: +/* Line 1778 of yacc.c */ +#line 585 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; - case 110: - -/* Line 1455 of yacc.c */ -#line 570 "a.y" + case 114: +/* Line 1778 of yacc.c */ +#line 589 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; - case 111: - -/* Line 1455 of yacc.c */ -#line 574 "a.y" + case 115: +/* Line 1778 of yacc.c */ +#line 593 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; - case 112: - -/* Line 1455 of yacc.c */ -#line 578 "a.y" + case 116: +/* Line 1778 of yacc.c */ +#line 597 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; - case 113: - -/* Line 1455 of yacc.c */ -#line 582 "a.y" + case 117: +/* Line 1778 of yacc.c */ +#line 601 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; - case 115: - -/* Line 1455 of yacc.c */ -#line 589 "a.y" + case 119: +/* Line 1778 of yacc.c */ +#line 608 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; - case 116: - -/* Line 1455 of yacc.c */ -#line 593 "a.y" + case 120: +/* Line 1778 of yacc.c */ +#line 612 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; - case 117: - -/* Line 1455 of yacc.c */ -#line 597 "a.y" + case 121: +/* Line 1778 of yacc.c */ +#line 616 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; - case 118: - -/* Line 1455 of yacc.c */ -#line 601 "a.y" + case 122: +/* Line 1778 of yacc.c */ +#line 620 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; - case 119: - -/* Line 1455 of yacc.c */ -#line 605 "a.y" + case 123: +/* Line 1778 of yacc.c */ +#line 624 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; - case 120: - -/* Line 1455 of yacc.c */ -#line 609 "a.y" + case 124: +/* Line 1778 of yacc.c */ +#line 628 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; - case 121: - -/* Line 1455 of yacc.c */ -#line 613 "a.y" + case 125: +/* Line 1778 of yacc.c */ +#line 632 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; - case 122: - -/* Line 1455 of yacc.c */ -#line 617 "a.y" + case 126: +/* Line 1778 of yacc.c */ +#line 636 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; - case 123: - -/* Line 1455 of yacc.c */ -#line 621 "a.y" + case 127: +/* Line 1778 of yacc.c */ +#line 640 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; - case 124: - -/* Line 1455 of yacc.c */ -#line 625 "a.y" + case 128: +/* Line 1778 of yacc.c */ +#line 644 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } break; - -/* Line 1455 of yacc.c */ -#line 2643 "y.tab.c" +/* Line 1778 of yacc.c */ +#line 2611 "y.tab.c" default: break; } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -2669,6 +2648,10 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -2676,37 +2659,36 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) { - YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); - if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) - { - YYSIZE_T yyalloc = 2 * yysize; - if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) - yyalloc = YYSTACK_ALLOC_MAXIMUM; - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yyalloc); - if (yymsg) - yymsg_alloc = yyalloc; - else - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - } - } - - if (0 < yysize && yysize <= yymsg_alloc) - { - (void) yysyntax_error (yymsg, yystate, yychar); - yyerror (yymsg); - } - else - { - yyerror (YY_("syntax error")); - if (yysize != 0) - goto yyexhaustedlab; - } + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; } +# undef YYSYNTAX_ERROR #endif } @@ -2765,7 +2747,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (yyn != YYPACT_NINF) + if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -2788,7 +2770,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ @@ -2812,7 +2796,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -2824,8 +2808,13 @@ yyexhaustedlab: yyreturn: if (yychar != YYEMPTY) - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -2849,4 +2838,3 @@ yyreturn: } - diff --git a/src/cmd/8a/y.tab.h b/src/cmd/8a/y.tab.h index 69a966a4b..621aba79f 100644 --- a/src/cmd/8a/y.tab.h +++ b/src/cmd/8a/y.tab.h @@ -1,10 +1,8 @@ +/* A Bison parser, made by GNU Bison 2.6.5. */ -/* A Bison parser, made by GNU Bison 2.4.1. */ - -/* Skeleton interface for Bison's Yacc-like parsers in C +/* Bison interface for Yacc-like parsers in C - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 - Free Software Foundation, Inc. + Copyright (C) 1984, 1989-1990, 2000-2012 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,6 +30,15 @@ This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ +#ifndef YY_YY_Y_TAB_H_INCLUDED +# define YY_YY_Y_TAB_H_INCLUDED +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif /* Tokens. */ #ifndef YYTOKENTYPE @@ -53,20 +60,22 @@ LTYPEM = 269, LTYPEI = 270, LTYPEG = 271, - LCONST = 272, - LFP = 273, - LPC = 274, - LSB = 275, - LBREG = 276, - LLREG = 277, - LSREG = 278, - LFREG = 279, - LFCONST = 280, - LSCONST = 281, - LSP = 282, - LNAME = 283, - LLAB = 284, - LVAR = 285 + LTYPEXC = 272, + LCONST = 273, + LFP = 274, + LPC = 275, + LSB = 276, + LBREG = 277, + LLREG = 278, + LSREG = 279, + LFREG = 280, + LXREG = 281, + LFCONST = 282, + LSCONST = 283, + LSP = 284, + LNAME = 285, + LLAB = 286, + LVAR = 287 }; #endif /* Tokens. */ @@ -84,29 +93,29 @@ #define LTYPEM 269 #define LTYPEI 270 #define LTYPEG 271 -#define LCONST 272 -#define LFP 273 -#define LPC 274 -#define LSB 275 -#define LBREG 276 -#define LLREG 277 -#define LSREG 278 -#define LFREG 279 -#define LFCONST 280 -#define LSCONST 281 -#define LSP 282 -#define LNAME 283 -#define LLAB 284 -#define LVAR 285 - +#define LTYPEXC 272 +#define LCONST 273 +#define LFP 274 +#define LPC 275 +#define LSB 276 +#define LBREG 277 +#define LLREG 278 +#define LSREG 279 +#define LFREG 280 +#define LXREG 281 +#define LFCONST 282 +#define LSCONST 283 +#define LSP 284 +#define LNAME 285 +#define LLAB 286 +#define LVAR 287 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE { - -/* Line 1676 of yacc.c */ +/* Line 2042 of yacc.c */ #line 37 "a.y" Sym *sym; @@ -121,9 +130,8 @@ typedef union YYSTYPE Gen2 gen2; - -/* Line 1676 of yacc.c */ -#line 127 "y.tab.h" +/* Line 2042 of yacc.c */ +#line 135 "y.tab.h" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ @@ -132,4 +140,18 @@ typedef union YYSTYPE extern YYSTYPE yylval; +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ +#endif /* !YY_YY_Y_TAB_H_INCLUDED */ diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index 869d31ace..78eb7eced 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -277,6 +277,18 @@ cgen(Node *n, Node *nn) break; } } + if(n->op == OOR && l->op == OASHL && r->op == OLSHR + && l->right->op == OCONST && r->right->op == OCONST + && l->left->op == ONAME && r->left->op == ONAME + && l->left->sym == r->left->sym + && l->right->vconst + r->right->vconst == 8 * l->left->type->width) { + regalloc(&nod, l->left, nn); + cgen(l->left, &nod); + gopcode(OROTL, n->type, l->right, &nod); + gmove(&nod, nn); + regfree(&nod); + break; + } if(n->op == OADD && l->op == OASHL && l->right->op == OCONST && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) { c = l->right->vconst; @@ -1703,6 +1715,7 @@ copy: } } + v = w == 8; if(n->complex >= FNX && nn != nil && nn->complex >= FNX) { t = nn->type; nn->type = types[TLONG]; @@ -1728,8 +1741,28 @@ copy: } x = 0; - v = w == 8; if(v) { + if(nn != nil && nn->complex >= FNX) { + t = nn->type; + nn->type = types[TLONG]; + regialloc(&nod2, nn, Z); + lcgen(nn, &nod2); + nn->type = t; + + nod2.type = typ(TIND, t); + + nod1 = nod2; + nod1.op = OIND; + nod1.left = &nod2; + nod1.right = Z; + nod1.complex = 1; + nod1.type = t; + + sugen(n, &nod1, w); + regfree(&nod2); + return; + } + c = cursafe; if(n->left != Z && n->left->complex >= FNX && n->right != Z && n->right->complex >= FNX) { diff --git a/src/cmd/8c/doc.go b/src/cmd/8c/doc.go index e3aae857f..0d07db14d 100644 --- a/src/cmd/8c/doc.go +++ b/src/cmd/8c/doc.go @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* 8c is a version of the Plan 9 C compiler. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2c + http://plan9.bell-labs.com/magic/man2html/1/8c Its target architecture is the x86, referred to by these tools for historical reasons as 386. */ -package documentation +package main diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index 4a57f5d3c..bdf981b4c 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -297,6 +297,7 @@ void gbranch(int); void patch(Prog*, int32); int sconst(Node*); void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); /* * swt.c diff --git a/src/cmd/8c/list.c b/src/cmd/8c/list.c index c422905cd..8506e08ef 100644 --- a/src/cmd/8c/list.c +++ b/src/cmd/8c/list.c @@ -93,7 +93,7 @@ Pconv(Fmt *fp) break; default: - sprint(str, "(%L) %A %D,%lD", + sprint(str, "(%L) %A %D,%D", p->lineno, p->as, &p->from, &p->to); break; } @@ -118,6 +118,17 @@ Dconv(Fmt *fp) a = va_arg(fp->args, Adr*); i = a->type; + + if(fp->flags & FmtLong) { + if(i == D_CONST2) + sprint(str, "$%d-%d", a->offset, a->offset2); + else { + // ATEXT dst is not constant + sprint(str, "!!%D", a); + } + goto brk; + } + if(i >= D_INDIR) { if(a->offset) sprint(str, "%d(%R)", a->offset, i-D_INDIR); @@ -126,7 +137,6 @@ Dconv(Fmt *fp) goto brk; } switch(i) { - default: if(a->offset) sprint(str, "$%d,%R", a->offset, i); @@ -139,7 +149,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - sprint(str, "%d(PC)", a->offset-pc); + sprint(str, "%d", a->offset); break; case D_EXTERN: @@ -147,12 +157,14 @@ Dconv(Fmt *fp) break; case D_STATIC: - sprint(str, "%s<>+%d(SB)", a->sym->name, - a->offset); + sprint(str, "%s<>+%d(SB)", a->sym->name, a->offset); break; case D_AUTO: - sprint(str, "%s+%d(SP)", a->sym->name, a->offset); + if(a->sym) + sprint(str, "%s+%d(SP)", a->sym->name, a->offset); + else + sprint(str, "%d(SP)", a->offset); break; case D_PARAM: @@ -167,7 +179,10 @@ Dconv(Fmt *fp) break; case D_CONST2: - sprint(str, "$%d-%d", a->offset, a->offset2); + if(!(fp->flags & FmtLong)) { + // D_CONST2 outside of ATEXT should not happen + sprint(str, "!!$%d-%d", a->offset, a->offset2); + } break; case D_FCONST: @@ -197,7 +212,7 @@ conv: char* regstr[] = { - "AL", /*[D_AL]*/ + "AL", /* [D_AL] */ "CL", "DL", "BL", @@ -206,7 +221,7 @@ char* regstr[] = "DH", "BH", - "AX", /*[D_AX]*/ + "AX", /* [D_AX] */ "CX", "DX", "BX", @@ -215,7 +230,7 @@ char* regstr[] = "SI", "DI", - "F0", /*[D_F0]*/ + "F0", /* [D_F0] */ "F1", "F2", "F3", @@ -224,20 +239,20 @@ char* regstr[] = "F6", "F7", - "CS", /*[D_CS]*/ + "CS", /* [D_CS] */ "SS", "DS", "ES", "FS", "GS", - "GDTR", /*[D_GDTR]*/ - "IDTR", /*[D_IDTR]*/ - "LDTR", /*[D_LDTR]*/ - "MSW", /*[D_MSW] */ - "TASK", /*[D_TASK]*/ + "GDTR", /* [D_GDTR] */ + "IDTR", /* [D_IDTR] */ + "LDTR", /* [D_LDTR] */ + "MSW", /* [D_MSW] */ + "TASK", /* [D_TASK] */ - "CR0", /*[D_CR]*/ + "CR0", /* [D_CR] */ "CR1", "CR2", "CR3", @@ -246,7 +261,7 @@ char* regstr[] = "CR6", "CR7", - "DR0", /*[D_DR]*/ + "DR0", /* [D_DR] */ "DR1", "DR2", "DR3", @@ -255,7 +270,7 @@ char* regstr[] = "DR6", "DR7", - "TR0", /*[D_TR]*/ + "TR0", /* [D_TR] */ "TR1", "TR2", "TR3", @@ -264,7 +279,16 @@ char* regstr[] = "TR6", "TR7", - "NONE", /*[D_NONE]*/ + "X0", /* [D_X0] */ + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + + "NONE", /* [D_NONE] */ }; int diff --git a/src/cmd/8c/mul.c b/src/cmd/8c/mul.c index a0742807e..9955e762f 100644 --- a/src/cmd/8c/mul.c +++ b/src/cmd/8c/mul.c @@ -35,17 +35,17 @@ typedef struct Mparam Mparam; struct Malg { - char vals[10]; + schar vals[10]; }; struct Mparam { uint32 value; - char alg; + schar alg; char neg; char shift; char arg; - char off; + schar off; }; static Mparam multab[32]; @@ -101,7 +101,7 @@ mulparam(uint32 m, Mparam *mp) { int c, i, j, n, o, q, s; int bc, bi, bn, bo, bq, bs, bt; - char *p; + schar *p; int32 u; uint32 t; diff --git a/src/cmd/8c/peep.c b/src/cmd/8c/peep.c index 9511a5579..9c3e9a5af 100644 --- a/src/cmd/8c/peep.c +++ b/src/cmd/8c/peep.c @@ -602,6 +602,12 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPL: /* read only */ case ACMPW: case ACMPB: + + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: + case AFCOMB: case AFCOMBP: diff --git a/src/cmd/8c/reg.c b/src/cmd/8c/reg.c index 6ba07bed2..6c87d70a5 100644 --- a/src/cmd/8c/reg.c +++ b/src/cmd/8c/reg.c @@ -30,6 +30,8 @@ #include "gc.h" +static void fixjmp(Reg*); + Reg* rega(void) { @@ -148,7 +150,6 @@ regopt(Prog *p) r->p1 = R; r1->s1 = R; } - bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -182,6 +183,10 @@ regopt(Prog *p) case ACMPB: case ACMPL: case ACMPW: + case APREFETCHT0: + case APREFETCHT1: + case APREFETCHT2: + case APREFETCHNTA: for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; break; @@ -372,6 +377,12 @@ regopt(Prog *p) } /* + * pass 2.1 + * fix jumps + */ + fixjmp(firstr); + + /* * pass 2.5 * find looping structure */ @@ -543,6 +554,13 @@ brk: if(!debug['R'] || debug['P']) peep(); + if(debug['R'] && debug['v']) { + print("after pass 7 (peep)\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } + /* * pass 8 * recalculate pc @@ -596,6 +614,14 @@ brk: while(p->link && p->link->as == ANOP) p->link = p->link->link; } + + if(debug['R'] && debug['v']) { + print("after pass 8 (fixup pc)\n"); + for(p1=firstr->prog; p1!=P; p1=p1->link) + print("%P\n", p1); + print("\n"); + } + if(r1 != R) { r1->link = freer; freer = firstr; @@ -1285,3 +1311,126 @@ BtoR(int32 b) return 0; return bitno(b) + D_AX; } + +/* what instruction does a JMP to p eventually land on? */ +static Reg* +chasejmp(Reg *r, int *jmploop) +{ + int n; + + n = 0; + for(; r; r=r->s2) { + if(r->prog->as != AJMP || r->prog->to.type != D_BRANCH) + break; + if(++n > 10) { + *jmploop = 1; + break; + } + } + return r; +} + +/* mark all code reachable from firstp as alive */ +static void +mark(Reg *firstr) +{ + Reg *r; + Prog *p; + + for(r=firstr; r; r=r->link) { + if(r->active) + break; + r->active = 1; + p = r->prog; + if(p->as != ACALL && p->to.type == D_BRANCH) + mark(r->s2); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) + break; + } +} + +/* + * the code generator depends on being able to write out JMP + * instructions that it can jump to now but fill in later. + * the linker will resolve them nicely, but they make the code + * longer and more difficult to follow during debugging. + * remove them. + */ +static void +fixjmp(Reg *firstr) +{ + int jmploop; + Reg *r; + Prog *p; + + if(debug['R'] && debug['v']) + print("\nfixjmp\n"); + + // pass 1: resolve jump to AJMP, mark all code as dead. + jmploop = 0; + for(r=firstr; r; r=r->link) { + p = r->prog; + if(debug['R'] && debug['v']) + print("%04d %P\n", r->pc, p); + if(p->as != ACALL && p->to.type == D_BRANCH && r->s2 && r->s2->prog->as == AJMP) { + r->s2 = chasejmp(r->s2, &jmploop); + p->to.offset = r->s2->pc; + if(debug['R'] && debug['v']) + print("->%P\n", p); + } + r->active = 0; + } + if(debug['R'] && debug['v']) + print("\n"); + + // pass 2: mark all reachable code alive + mark(firstr); + + // pass 3: delete dead code (mostly JMPs). + for(r=firstr; r; r=r->link) { + if(!r->active) { + p = r->prog; + if(p->link == P && p->as == ARET && r->p1 && r->p1->prog->as != ARET) { + // This is the final ARET, and the code so far doesn't have one. + // Let it stay. + } else { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // pass 4: elide JMP to next instruction. + // only safe if there are no jumps to JMPs anymore. + if(!jmploop) { + for(r=firstr; r; r=r->link) { + p = r->prog; + if(p->as == AJMP && p->to.type == D_BRANCH && r->s2 == r->link) { + if(debug['R'] && debug['v']) + print("del %04d %P\n", r->pc, p); + p->as = ANOP; + } + } + } + + // fix back pointers. + for(r=firstr; r; r=r->link) { + r->p2 = R; + r->p2link = R; + } + for(r=firstr; r; r=r->link) { + if(r->s2) { + r->p2link = r->s2->p2; + r->s2->p2 = r; + } + } + + if(debug['R'] && debug['v']) { + print("\n"); + for(r=firstr; r; r=r->link) + print("%04d %P\n", r->pc, r->prog); + print("\n"); + } +} + diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index f1ca4c25f..d331eee1a 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -251,18 +251,12 @@ outcode(void) Binit(&b, f, OWRITE); Bprint(&b, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); - if(ndynimp > 0 || ndynexp > 0) { - int i; - + if(pragcgobuf.to > pragcgobuf.start) { Bprint(&b, "\n"); Bprint(&b, "$$ // exports\n\n"); Bprint(&b, "$$ // local types\n\n"); - Bprint(&b, "$$ // dynimport\n"); - for(i=0; i<ndynimp; i++) - Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path); - Bprint(&b, "\n$$ // dynexport\n"); - for(i=0; i<ndynexp; i++) - Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote); + Bprint(&b, "$$ // cgo\n"); + Bprint(&b, "%s", fmtstrflush(&pragcgobuf)); Bprint(&b, "\n$$\n\n"); } Bprint(&b, "!\n"); @@ -343,12 +337,38 @@ outhist(Biobuf *b) char *p, *q, *op, c; Prog pg; int n; + char *tofree; + static int first = 1; + static char *goroot, *goroot_final; + + if(first) { + // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. + first = 0; + goroot = getenv("GOROOT"); + goroot_final = getenv("GOROOT_FINAL"); + if(goroot == nil) + goroot = ""; + if(goroot_final == nil) + goroot_final = goroot; + if(strcmp(goroot, goroot_final) == 0) { + goroot = nil; + goroot_final = nil; + } + } + tofree = nil; pg = zprog; pg.as = AHISTORY; c = pathchar(); for(h = hist; h != H; h = h->link) { p = h->name; + if(p != nil && goroot != nil) { + n = strlen(goroot); + if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { + tofree = smprint("%s%s", goroot_final, p+n); + p = tofree; + } + } op = 0; if(systemtype(Windows) && p && p[1] == ':'){ c = p[2]; @@ -404,6 +424,11 @@ outhist(Biobuf *b) Bputc(b, pg.lineno>>24); zaddr(b, &pg.from, 0); zaddr(b, &pg.to, 0); + + if(tofree) { + free(tofree); + tofree = nil; + } } } @@ -597,8 +622,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) int32 maxround(int32 max, int32 v) { - v += SZ_LONG-1; + v = xround(v, SZ_LONG); if(v > max) - max = xround(v, SZ_LONG); + return v; return max; } diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 3a08da7cd..1b7617bc5 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -1253,6 +1253,14 @@ gopcode(int o, Type *ty, Node *f, Node *t) a = ASALW; break; + case OROTL: + a = AROLL; + if(et == TCHAR || et == TUCHAR) + a = AROLB; + if(et == TSHORT || et == TUSHORT) + a = AROLW; + break; + case OFUNC: a = ACALL; break; @@ -1383,6 +1391,21 @@ gpseudo(int a, Sym *s, Node *n) pc--; } +void +gprefetch(Node *n) +{ + Node n1; + + if(strcmp(getgo386(), "sse2") != 0) // assume no prefetch on old machines + return; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(APREFETCHNTA, &n1, Z); + regfree(&n1); +} + int sconst(Node *n) { diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 48619ac73..0b2f2b76e 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -49,7 +49,7 @@ mfree(Node *n) void cgen(Node *n, Node *res) { - Node *nl, *nr, *r, n1, n2, nt, f0, f1; + Node *nl, *nr, *r, n1, n2, nt; Prog *p1, *p2, *p3; int a; @@ -63,9 +63,26 @@ cgen(Node *n, Node *res) if(res == N || res->type == T) fatal("cgen: res nil"); - // inline slices - if(cgen_inline(n, res)) + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); + return; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); return; + } while(n->op == OCONVNOP) n = n->left; @@ -160,6 +177,7 @@ cgen(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -170,8 +188,10 @@ cgen(Node *n, Node *res) } } - if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) - goto flt; + if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) { + cgen_float(n, res); + return; + } switch(n->op) { default: @@ -195,12 +215,12 @@ cgen(Node *n, Node *res) case OGE: case OGT: case ONOT: - p1 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); p2 = pc; gmove(nodbool(1), res); - p3 = gbranch(AJMP, T); + p3 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n, 1, p2); + bgen(n, 1, 0, p2); gmove(nodbool(0), res); patch(p3, pc); return; @@ -232,6 +252,10 @@ cgen(Node *n, Node *res) a = optoas(n->op, nl->type); goto abop; + case OHMUL: + cgen_hmul(nl, nr, res); + break; + case OCONV: if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) { cgen(nl, res); @@ -274,7 +298,7 @@ cgen(Node *n, Node *res) nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -308,7 +332,7 @@ cgen(Node *n, Node *res) nodconst(&n2, types[tptr], 0); gins(optoas(OCMP, types[tptr]), &n1, &n2); - p1 = gbranch(optoas(OEQ, types[tptr]), T); + p1 = gbranch(optoas(OEQ, types[tptr]), T, -1); n2 = n1; n2.op = OINDREG; @@ -324,9 +348,8 @@ cgen(Node *n, Node *res) } if(isslice(nl->type)) { igen(nl, &n1, res); - n1.op = OINDREG; n1.type = types[TUINT32]; - n1.xoffset = Array_cap; + n1.xoffset += Array_cap; gmove(&n1, res); regfree(&n1); break; @@ -360,20 +383,29 @@ cgen(Node *n, Node *res) case OLSH: case ORSH: - cgen_shift(n->op, nl, nr, res); + case OLROT: + cgen_shift(n->op, n->bounded, nl, nr, res); break; } return; sbop: // symmetric binary - if(nl->ullman < nr->ullman) { + if(nl->ullman < nr->ullman || nl->op == OLITERAL) { r = nl; nl = nr; nr = r; } abop: // asymmetric binary - if(nl->ullman >= nr->ullman) { + if(smallintconst(nr)) { + mgen(nl, &n1, res); + regalloc(&n2, nl->type, &n1); + gmove(&n1, &n2); + gins(a, nr, &n2); + gmove(&n2, res); + regfree(&n2); + mfree(&n1); + } else if(nl->ullman >= nr->ullman) { tempname(&nt, nl->type); cgen(nl, &nt); mgen(nr, &n2, N); @@ -401,69 +433,42 @@ uop: // unary gins(a, N, &n1); gmove(&n1, res); return; - -flt: // floating-point. 387 (not SSE2) to interoperate with 8c - nodreg(&f0, nl->type, D_F0); - nodreg(&f1, n->type, D_F0+1); - if(nr != N) - goto flt2; - - // unary - cgen(nl, &f0); - if(n->op != OCONV && n->op != OPLUS) - gins(foptoas(n->op, n->type, 0), N, N); - gmove(&f0, res); - return; - -flt2: // binary - if(nl->ullman >= nr->ullman) { - cgen(nl, &f0); - if(nr->addable) - gins(foptoas(n->op, n->type, 0), nr, &f0); - else { - cgen(nr, &f0); - gins(foptoas(n->op, n->type, Fpop), &f0, &f1); - } - } else { - cgen(nr, &f0); - if(nl->addable) - gins(foptoas(n->op, n->type, Frev), nl, &f0); - else { - cgen(nl, &f0); - gins(foptoas(n->op, n->type, Frev|Fpop), &f0, &f1); - } - } - gmove(&f0, res); - return; } /* - * generate array index into res. - * n might be any size; res is 32-bit. + * generate an addressable node in res, containing the value of n. + * n is an array index, and might be any size; res width is <= 32-bit. * returns Prog* to patch to panic call. */ -Prog* -cgenindex(Node *n, Node *res) +static Prog* +igenindex(Node *n, Node *res, int bounded) { Node tmp, lo, hi, zero; if(!is64(n->type)) { - cgen(n, res); + if(n->addable) { + // nothing to do. + *res = *n; + } else { + tempname(res, types[TUINT32]); + cgen(n, res); + } return nil; } tempname(&tmp, types[TINT64]); cgen(n, &tmp); split64(&tmp, &lo, &hi); + tempname(res, types[TUINT32]); gmove(&lo, res); - if(debug['B']) { + if(bounded) { splitclean(); return nil; } nodconst(&zero, types[TINT32], 0); gins(ACMPL, &hi, &zero); splitclean(); - return gbranch(AJNE, T); + return gbranch(AJNE, T, +1); } /* @@ -474,11 +479,12 @@ void agen(Node *n, Node *res) { Node *nl, *nr; - Node n1, n2, n3, n4, tmp; + Node n1, n2, n3, n4, tmp, nlen; Type *t; uint32 w; uint64 v; Prog *p1, *p2; + int bounded; if(debug['g']) { dump("\nagen-res", res); @@ -490,6 +496,20 @@ agen(Node *n, Node *res) while(n->op == OCONVNOP) n = n->left; + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + gins(ALEAL, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + return; + } + // addressable var is easy if(n->addable) { if(n->op == OREGISTER) @@ -524,113 +544,136 @@ agen(Node *n, Node *res) cgen_aret(n, res); break; + case OSLICE: + case OSLICEARR: + case OSLICESTR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + case OINDEX: p2 = nil; // to be patched to panicindex. w = n->type->width; + bounded = debug['B'] || n->bounded; if(nr->addable) { - if(!isconst(nr, CTINT)) - tempname(&tmp, types[TINT32]); + // Generate &nl first, and move nr into register. if(!isconst(nl, CTSTR)) - agenr(nl, &n3, res); + igen(nl, &n3, res); if(!isconst(nr, CTINT)) { - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } } else if(nl->addable) { + // Generate nr first, and move &nl into register. if(!isconst(nr, CTINT)) { - tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); regalloc(&n1, tmp.type, N); gmove(&tmp, &n1); } - if(!isconst(nl, CTSTR)) { - regalloc(&n3, types[tptr], res); - agen(nl, &n3); - } + if(!isconst(nl, CTSTR)) + igen(nl, &n3, res); } else { - tempname(&tmp, types[TINT32]); - p2 = cgenindex(nr, &tmp); + p2 = igenindex(nr, &tmp, bounded); nr = &tmp; if(!isconst(nl, CTSTR)) - agenr(nl, &n3, res); + igen(nl, &n3, res); regalloc(&n1, tmp.type, N); gins(optoas(OAS, tmp.type), &tmp, &n1); } - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) + // For fixed array we really want the pointer in n3. + if(isfixedarray(nl->type)) { + regalloc(&n2, types[tptr], &n3); + agen(&n3, &n2); + regfree(&n3); + n3 = n2; + } + + // &a[0] is in n3 (allocated in res) + // i is in n1 (if not constant) + // len(a) is in nlen (if needed) // w is width // explicit check for nil if array is large enough // that we might derive too big a pointer. if(isfixedarray(nl->type) && nl->type->width >= unmappedzero) { - regalloc(&n4, types[tptr], &n3); - gmove(&n3, &n4); + n4 = n3; n4.op = OINDREG; n4.type = types[TUINT8]; n4.xoffset = 0; gins(ATESTB, nodintconst(0), &n4); - regfree(&n4); } // constant index if(isconst(nr, CTINT)) { if(isconst(nl, CTSTR)) - fatal("constant string constant index"); + fatal("constant string constant index"); // front end should handle v = mpgetfix(nr->val.u.xval); if(isslice(nl->type) || nl->type->etype == TSTRING) { - if(!debug['B'] && !n->etype) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; + if(!debug['B'] && !n->bounded) { + nlen = n3; + nlen.type = types[TUINT32]; + nlen.xoffset += Array_nel; nodconst(&n2, types[TUINT32], v); - gins(optoas(OCMP, types[TUINT32]), &n1, &n2); - p1 = gbranch(optoas(OGT, types[TUINT32]), T); - ginscall(panicindex, 0); + gins(optoas(OCMP, types[TUINT32]), &nlen, &n2); + p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1); + ginscall(panicindex, -1); patch(p1, pc); } - - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); } + // Load base pointer in n2 = n3. + regalloc(&n2, types[tptr], &n3); + n3.type = types[tptr]; + n3.xoffset += Array_array; + gmove(&n3, &n2); + regfree(&n3); if (v*w != 0) { - nodconst(&n2, types[tptr], v*w); - gins(optoas(OADD, types[tptr]), &n2, &n3); + nodconst(&n1, types[tptr], v*w); + gins(optoas(OADD, types[tptr]), &n1, &n2); } - gmove(&n3, res); - regfree(&n3); + gmove(&n2, res); + regfree(&n2); break; } - regalloc(&n2, types[TINT32], &n1); // i + // i is in register n1, extend to 32 bits. + t = types[TUINT32]; + if(issigned[n1.type->etype]) + t = types[TINT32]; + + regalloc(&n2, t, &n1); // i gmove(&n1, &n2); regfree(&n1); - if(!debug['B'] && !n->etype) { + if(!debug['B'] && !n->bounded) { // check bounds - if(isconst(nl, CTSTR)) - nodconst(&n1, types[TUINT32], nl->val.u.sval->len); - else if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_nel; - } else - nodconst(&n1, types[TUINT32], nl->type->bound); - gins(optoas(OCMP, types[TUINT32]), &n2, &n1); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + t = types[TUINT32]; + if(isconst(nl, CTSTR)) { + nodconst(&nlen, t, nl->val.u.sval->len); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { + nlen = n3; + nlen.type = t; + nlen.xoffset += Array_nel; + } else { + nodconst(&nlen, t, nl->type->bound); + } + gins(optoas(OCMP, t), &n2, &nlen); + p1 = gbranch(optoas(OLT, t), T, +1); if(p2) patch(p2, pc); - ginscall(panicindex, 0); + ginscall(panicindex, -1); patch(p1, pc); } - + if(isconst(nl, CTSTR)) { regalloc(&n3, types[tptr], res); p1 = gins(ALEAL, N, &n3); @@ -640,24 +683,27 @@ agen(Node *n, Node *res) goto indexdone; } + // Load base pointer in n3. + regalloc(&tmp, types[tptr], &n3); if(isslice(nl->type) || nl->type->etype == TSTRING) { - n1 = n3; - n1.op = OINDREG; - n1.type = types[tptr]; - n1.xoffset = Array_array; - gmove(&n1, &n3); + n3.type = types[tptr]; + n3.xoffset += Array_array; + gmove(&n3, &tmp); } + regfree(&n3); + n3 = tmp; if(w == 0) { // nothing to do } else if(w == 1 || w == 2 || w == 4 || w == 8) { + // LEAL (n3)(n2*w), n3 p1 = gins(ALEAL, &n2, &n3); p1->from.scale = w; p1->from.index = p1->from.type; p1->from.type = p1->to.type + D_INDIR; } else { - nodconst(&n1, types[TUINT32], w); - gins(optoas(OMUL, types[TUINT32]), &n1, &n2); + nodconst(&tmp, types[TUINT32], w); + gins(optoas(OMUL, types[TUINT32]), &tmp, &n2); gins(optoas(OADD, types[tptr]), &n2, &n3); } @@ -734,20 +780,82 @@ agen(Node *n, Node *res) void igen(Node *n, Node *a, Node *res) { - Node n1; Type *fp; Iter flist; - + Node n1; + + if(debug['g']) { + dump("\nigen-n", n); + } switch(n->op) { case ONAME: if((n->class&PHEAP) || n->class == PPARAMREF) break; *a = *n; return; - + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != D_SP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + return; + + case ODOTPTR: + switch(n->left->op) { + case ODOT: + case ODOTPTR: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + // igen-able nodes. + igen(n->left, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, types[tptr], res); + cgen(n->left, a); + } + if(n->xoffset != 0) { + // explicit check for nil if struct is large enough + // that we might derive too big a pointer. + if(n->left->type->type->width >= unmappedzero) { + n1 = *a; + n1.op = OINDREG; + n1.type = types[TUINT8]; + n1.xoffset = 0; + gins(ATESTB, nodintconst(0), &n1); + } + } + a->op = OINDREG; + a->xoffset += n->xoffset; + a->type = n->type; + return; + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + switch(n->op) { + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } fp = structfirst(&flist, getoutarg(n->left->type)); - cgen_call(n, 0); memset(a, 0, sizeof *a); a->op = OINDREG; a->val.u.reg = D_SP; @@ -771,33 +879,15 @@ igen(Node *n, Node *a, Node *res) } /* - * generate: - * newreg = &n; - * - * caller must regfree(a). - */ -void -agenr(Node *n, Node *a, Node *res) -{ - Node n1; - - tempname(&n1, types[tptr]); - agen(n, &n1); - regalloc(a, types[tptr], res); - gmove(&n1, a); -} - -/* * branch gen * if(n == true) goto to; */ void -bgen(Node *n, int true, Prog *to) +bgen(Node *n, int true, int likely, Prog *to) { int et, a; Node *nl, *nr, *r; - Node n1, n2, tmp, t1, t2, ax; - NodeList *ll; + Node n1, n2, tmp; Prog *p1, *p2; if(debug['g']) { @@ -822,8 +912,14 @@ bgen(Node *n, int true, Prog *to) patch(gins(AEND, N, N), to); return; } + nl = n->left; nr = N; + if(nl != N && isfloat[nl->type->etype]) { + bgen_float(n, true, likely, to); + return; + } + switch(n->op) { default: def: @@ -834,14 +930,14 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); regfree(&n1); return; case OLITERAL: // need to ask if it is bool? if(!true == !n->val.u.bval) - patch(gbranch(AJMP, T), to); + patch(gbranch(AJMP, T, 0), to); return; case ONAME: @@ -852,7 +948,7 @@ bgen(Node *n, int true, Prog *to) a = AJNE; if(!true) a = AJEQ; - patch(gbranch(a, n->type), to); + patch(gbranch(a, n->type, likely), to); return; case OANDAND: @@ -860,12 +956,12 @@ bgen(Node *n, int true, Prog *to) goto caseor; caseand: - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - bgen(n->left, !true, p2); - bgen(n->right, !true, p2); - p1 = gbranch(AJMP, T); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); patch(p1, to); patch(p2, pc); return; @@ -875,8 +971,8 @@ bgen(Node *n, int true, Prog *to) goto caseand; caseor: - bgen(n->left, true, to); - bgen(n->right, true, to); + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); return; case OEQ: @@ -897,7 +993,7 @@ bgen(Node *n, int true, Prog *to) switch(n->op) { case ONOT: - bgen(nl, !true, to); + bgen(nl, !true, likely, to); break; case OEQ: @@ -908,19 +1004,6 @@ bgen(Node *n, int true, Prog *to) case OGE: a = n->op; if(!true) { - if(isfloat[nl->type->etype]) { - // brcom is not valid on floats when NaN is involved. - p1 = gbranch(AJMP, T); - p2 = gbranch(AJMP, T); - patch(p1, pc); - ll = n->ninit; // avoid re-genning ninit - n->ninit = nil; - bgen(n, 1, p2); - n->ninit = ll; - patch(gbranch(AJMP, T), to); - patch(p2, pc); - break; - } a = brcom(a); true = !true; } @@ -936,19 +1019,16 @@ bgen(Node *n, int true, Prog *to) if(isslice(nl->type)) { // front end should only leave cmp to literal nil if((a != OEQ && a != ONE) || nr->op != OLITERAL) { - yyerror("illegal array comparison"); + yyerror("illegal slice comparison"); break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = Array_array; - n2.type = types[tptr]; + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } @@ -960,75 +1040,17 @@ bgen(Node *n, int true, Prog *to) break; } a = optoas(a, types[tptr]); - regalloc(&n1, types[tptr], N); - agen(nl, &n1); - n2 = n1; - n2.op = OINDREG; - n2.xoffset = 0; + igen(nl, &n1, N); + n1.type = types[tptr]; nodconst(&tmp, types[tptr], 0); - gins(optoas(OCMP, types[tptr]), &n2, &tmp); - patch(gbranch(a, types[tptr]), to); + gins(optoas(OCMP, types[tptr]), &n1, &tmp); + patch(gbranch(a, types[tptr], likely), to); regfree(&n1); break; } - if(isfloat[nr->type->etype]) { - a = brrev(a); // because the args are stacked - if(a == OGE || a == OGT) { - // only < and <= work right with NaN; reverse if needed - r = nr; - nr = nl; - nl = r; - a = brrev(a); - } - nodreg(&tmp, nr->type, D_F0); - nodreg(&n2, nr->type, D_F0 + 1); - nodreg(&ax, types[TUINT16], D_AX); - et = simsimtype(nr->type); - if(et == TFLOAT64) { - if(nl->ullman > nr->ullman) { - cgen(nl, &tmp); - cgen(nr, &tmp); - gins(AFXCHD, &tmp, &n2); - } else { - cgen(nr, &tmp); - cgen(nl, &tmp); - } - gins(AFUCOMIP, &tmp, &n2); - gins(AFMOVDP, &tmp, &tmp); // annoying pop but still better than STSW+SAHF - } else { - // TODO(rsc): The moves back and forth to memory - // here are for truncating the value to 32 bits. - // This handles 32-bit comparison but presumably - // all the other ops have the same problem. - // We need to figure out what the right general - // solution is, besides telling people to use float64. - tempname(&t1, types[TFLOAT32]); - tempname(&t2, types[TFLOAT32]); - cgen(nr, &t1); - cgen(nl, &t2); - gmove(&t2, &tmp); - gins(AFCOMFP, &t1, &tmp); - gins(AFSTSW, N, &ax); - gins(ASAHF, N, N); - } - if(a == OEQ) { - // neither NE nor P - p1 = gbranch(AJNE, T); - p2 = gbranch(AJPS, T); - patch(gbranch(AJMP, T), to); - patch(p1, pc); - patch(p2, pc); - } else if(a == ONE) { - // either NE or P - patch(gbranch(AJNE, T), to); - patch(gbranch(AJPS, T), to); - } else - patch(gbranch(optoas(a, nr->type), T), to); - break; - } if(iscomplex[nl->type->etype]) { - complexbool(a, nl, nr, true, to); + complexbool(a, nl, nr, true, likely, to); break; } @@ -1043,40 +1065,55 @@ bgen(Node *n, int true, Prog *to) cgen(nr, &n2); nr = &n2; } - cmp64(nl, nr, a, to); + cmp64(nl, nr, a, likely, to); break; } - a = optoas(a, nr->type); - if(nr->ullman >= UINF) { - tempname(&n1, nl->type); - tempname(&tmp, nr->type); - cgen(nl, &n1); - cgen(nr, &tmp); + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } regalloc(&n2, nr->type, N); - cgen(&tmp, &n2); + cgen(nr, &n2); + nr = &n2; goto cmp; } - tempname(&n1, nl->type); - cgen(nl, &n1); + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } if(smallintconst(nr)) { - gins(optoas(OCMP, nr->type), &n1, nr); - patch(gbranch(a, nr->type), to); + gins(optoas(OCMP, nr->type), nl, nr); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); break; } - tempname(&tmp, nr->type); - cgen(nr, &tmp); + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } regalloc(&n2, nr->type, N); - gmove(&tmp, &n2); + gmove(nr, &n2); + nr = &n2; cmp: - gins(optoas(OCMP, nr->type), &n1, &n2); - patch(gbranch(a, nr->type), to); - regfree(&n2); + gins(optoas(OCMP, nr->type), nl, nr); + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); + + if(nl->op == OREGISTER) + regfree(nl); + regfree(nr); break; } } @@ -1164,6 +1201,10 @@ sgen(Node *n, Node *res, int64 w) return; } + // Avoid taking the address for simple enough types. + if(componentgen(n, res)) + return; + // offset on the stack osrc = stkof(n); odst = stkof(res); @@ -1247,3 +1288,157 @@ sgen(Node *n, Node *res, int64 w) } } +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * nr is N when assigning a zero value. + * return 1 if can do, 0 if cant. + */ +int +componentgen(Node *nr, Node *nl) +{ + Node nodl, nodr; + int freel, freer; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + if(!isslice(nl->type)) + goto no; + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } + + switch(nl->type->etype) { + case TARRAY: + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[TUINT32]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --git a/src/cmd/8g/cgen64.c b/src/cmd/8g/cgen64.c index 8e568a0f9..dc50a409b 100644 --- a/src/cmd/8g/cgen64.c +++ b/src/cmd/8g/cgen64.c @@ -49,6 +49,7 @@ cgen64(Node *n, Node *res) case OADD: case OSUB: case OMUL: + case OLROT: case OLSH: case ORSH: case OAND: @@ -113,9 +114,9 @@ cgen64(Node *n, Node *res) // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. gins(AMOVL, &dx, &fx); gins(AORL, &ex, &fx); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, 0); gins(AMULL, &cx, N); // implicit &ax - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // full 64x64 -> 64, from 32x32 -> 64. @@ -131,6 +132,40 @@ cgen64(Node *n, Node *res) regfree(&ex); regfree(&fx); break; + + case OLROT: + // We only rotate by a constant c in [0,64). + // if c >= 32: + // lo, hi = hi, lo + // c -= 32 + // if c == 0: + // no-op + // else: + // t = hi + // shld hi:lo, c + // shld lo:t, c + v = mpgetfix(r->val.u.xval); + if(v >= 32) { + // reverse during load to do the first 32 bits of rotate + v -= 32; + gins(AMOVL, &lo1, &dx); + gins(AMOVL, &hi1, &ax); + } else { + gins(AMOVL, &lo1, &ax); + gins(AMOVL, &hi1, &dx); + } + if(v == 0) { + // done + } else { + gins(AMOVL, &dx, &cx); + p1 = gins(ASHLL, ncon(v), &dx); + p1->from.index = D_AX; // double-width shift + p1->from.scale = 0; + p1 = gins(ASHLL, ncon(v), &ax); + p1->from.index = D_CX; // double-width shift + p1->from.scale = 0; + } + break; case OLSH: if(r->op == OLITERAL) { @@ -178,7 +213,7 @@ cgen64(Node *n, Node *res) p1 = P; if(is64(r->type)) { gins(ACMPL, &hi2, ncon(0)); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, +1); gins(AMOVL, &lo2, &cx); } else { cx.type = types[TUINT32]; @@ -187,7 +222,7 @@ cgen64(Node *n, Node *res) // if shift count is >=64, zero value gins(ACMPL, &cx, ncon(64)); - p2 = gbranch(optoas(OLT, types[TUINT32]), T); + p2 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p1 != P) patch(p1, pc); gins(AXORL, &dx, &dx); @@ -196,11 +231,11 @@ cgen64(Node *n, Node *res) // if shift count is >= 32, zero low. gins(ACMPL, &cx, ncon(32)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); gins(AMOVL, &ax, &dx); gins(ASHLL, &cx, &dx); // SHLL only uses bottom 5 bits of count gins(AXORL, &ax, &ax); - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // general shift @@ -267,7 +302,7 @@ cgen64(Node *n, Node *res) p1 = P; if(is64(r->type)) { gins(ACMPL, &hi2, ncon(0)); - p1 = gbranch(AJNE, T); + p1 = gbranch(AJNE, T, +1); gins(AMOVL, &lo2, &cx); } else { cx.type = types[TUINT32]; @@ -276,7 +311,7 @@ cgen64(Node *n, Node *res) // if shift count is >=64, zero or sign-extend value gins(ACMPL, &cx, ncon(64)); - p2 = gbranch(optoas(OLT, types[TUINT32]), T); + p2 = gbranch(optoas(OLT, types[TUINT32]), T, +1); if(p1 != P) patch(p1, pc); if(hi1.type->etype == TINT32) { @@ -290,7 +325,7 @@ cgen64(Node *n, Node *res) // if shift count is >= 32, sign-extend hi. gins(ACMPL, &cx, ncon(32)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); gins(AMOVL, &dx, &ax); if(hi1.type->etype == TINT32) { gins(ASARL, &cx, &ax); // SARL only uses bottom 5 bits of count @@ -299,7 +334,7 @@ cgen64(Node *n, Node *res) gins(ASHRL, &cx, &ax); gins(AXORL, &dx, &dx); } - p2 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); // general shift @@ -427,7 +462,7 @@ out:; * nl is memory; nr is constant or memory. */ void -cmp64(Node *nl, Node *nr, int op, Prog *to) +cmp64(Node *nl, Node *nr, int op, int likely, Prog *to) { Node lo1, hi1, lo2, hi2, rr; Prog *br; @@ -457,14 +492,14 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jeq to // L: - br = gbranch(AJNE, T); + br = gbranch(AJNE, T, -likely); break; case ONE: // cmp hi // jne to // cmp lo // jne to - patch(gbranch(AJNE, T), to); + patch(gbranch(AJNE, T, likely), to); break; case OGE: case OGT: @@ -474,8 +509,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jge to (or jgt to) // L: - patch(gbranch(optoas(OGT, t), T), to); - br = gbranch(optoas(OLT, t), T); + patch(gbranch(optoas(OGT, t), T, likely), to); + br = gbranch(optoas(OLT, t), T, -likely); break; case OLE: case OLT: @@ -485,8 +520,8 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) // cmp lo // jle to (or jlt to) // L: - patch(gbranch(optoas(OLT, t), T), to); - br = gbranch(optoas(OGT, t), T); + patch(gbranch(optoas(OLT, t), T, likely), to); + br = gbranch(optoas(OGT, t), T, -likely); break; } @@ -502,7 +537,7 @@ cmp64(Node *nl, Node *nr, int op, Prog *to) } // jump again - patch(gbranch(optoas(op, t), T), to); + patch(gbranch(optoas(op, t), T, likely), to); // point first branch down here if appropriate if(br != P) diff --git a/src/cmd/8g/doc.go b/src/cmd/8g/doc.go index 6d678eac8..9e46dcad8 100644 --- a/src/cmd/8g/doc.go +++ b/src/cmd/8g/doc.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. +// +build ignore + /* 8g is the version of the gc compiler for the x86. @@ -10,4 +12,4 @@ The $GOARCH for these tools is 386. It reads .go files and outputs .8 files. The flags are documented in ../gc/doc.go. */ -package documentation +package main diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 4526a2efb..2c8aaa0a4 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -27,6 +27,7 @@ void betypeinit(void) { widthptr = 4; + widthint = 4; zprog.link = P; zprog.as = AGOK; diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 0a4f0ad2d..03c206aa9 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -16,9 +16,12 @@ struct Addr int32 offset; int32 offset2; - double dval; - Prog* branch; - char sval[NSNAME]; + union { + double dval; + vlong vval; + Prog* branch; + char sval[NSNAME]; + } u; Sym* gotype; Sym* sym; @@ -28,7 +31,6 @@ struct Addr uchar index; uchar etype; uchar scale; /* doubles as width in DATA op */ - uchar pun; /* dont register variable */ }; #define A ((Addr*)0) @@ -83,37 +85,38 @@ void cgen_proc(Node*, int); void cgen_callret(Node*, Node*); void cgen_div(int, Node*, Node*, Node*); void cgen_bmul(int, Node*, Node*, Node*); -void cgen_shift(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); +void cgen_float(Node*, Node*); +void bgen_float(Node *n, int true, int likely, Prog *to); void cgen_dcl(Node*); int needconvert(Type*, Type*); void genconv(Type*, Type*); void allocparams(void); -void checklabels(); +void checklabels(void); void ginscall(Node*, int); /* * cgen.c */ void agen(Node*, Node*); -void agenr(Node *n, Node *a, Node *res); void igen(Node*, Node*, Node*); vlong fieldoffset(Type*, Node*); -void bgen(Node*, int, Prog*); void sgen(Node*, Node*, int64); void gmove(Node*, Node*); Prog* gins(int, Node*, Node*); int samaddr(Node*, Node*); void naddr(Node*, Addr*, int); void cgen_aret(Node*, Node*); -int cgen_inline(Node*, Node*); Node* ncon(uint32); void mgen(Node*, Node*, Node*); void mfree(Node*); +int componentgen(Node*, Node*); /* * cgen64.c */ -void cmp64(Node*, Node*, int, Prog*); +void cmp64(Node*, Node*, int, int, Prog*); void cgen64(Node*, Node*); /* @@ -121,16 +124,13 @@ void cgen64(Node*, Node*); */ void clearp(Prog*); void proglist(void); -Prog* gbranch(int, Type*); +Prog* gbranch(int, Type*, int); Prog* prog(int); -void gaddoffset(Node*); void gconv(int, int); int conv2pt(Type*); vlong convvtox(vlong, int); void fnparam(Type*, int, int); Prog* gop(int, Node*, Node*, Node*); -void setconst(Addr*, vlong); -void setaddr(Addr*, Node*); int optoas(int, Type*); int foptoas(int, Type*, int); void ginit(void); @@ -142,18 +142,17 @@ void nodreg(Node*, Type*, int); void nodindreg(Node*, Type*, int); void nodconst(Node*, Type*, int64); void gconreg(int, vlong, int); -void datagostring(Strlit*, Addr*); -void datastring(char*, int, Addr*); void buildtxt(void); Plist* newplist(void); int isfat(Type*); void sudoclean(void); int sudoaddable(int, Node*, Addr*); int dotaddable(Node*, Node*); -void afunclit(Addr*); +void afunclit(Addr*, Node*); void split64(Node*, Node*, Node*); void splitclean(void); void nswap(Node*, Node*); +void gtrack(Sym*); /* * cplx.c @@ -161,7 +160,12 @@ void nswap(Node*, Node*); int complexop(Node*, Node*); void complexmove(Node*, Node*); void complexgen(Node*, Node*); -void complexbool(int, Node*, Node*, int, Prog*); + +/* + * gobj.c + */ +void datastring(char*, int, Addr*); +void datagostring(Strlit*, Addr*); /* * list.c diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 6a4570199..70148106c 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -27,6 +27,9 @@ void markautoused(Prog* p) { for (; p; p = p->link) { + if (p->as == ATYPE) + continue; + if (p->from.type == D_AUTO && p->from.node) p->from.node->used = 1; @@ -39,12 +42,21 @@ markautoused(Prog* p) void fixautoused(Prog* p) { - for (; p; p = p->link) { + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.type == D_AUTO && !p->from.node->used) { + *lp = p->link; + continue; + } + if (p->from.type == D_AUTO && p->from.node) p->from.offset += p->from.node->stkdelta; if (p->to.type == D_AUTO && p->to.node) p->to.offset += p->to.node->stkdelta; + + lp = &p->link; } } @@ -59,6 +71,10 @@ clearfat(Node *nl) dump("\nclearfat", nl); w = nl->type->width; + // Avoid taking the address for simple enough types. + if(componentgen(N, nl)) + return; + c = w % 4; // bytes q = w / 4; // quads @@ -90,15 +106,17 @@ clearfat(Node *nl) /* * generate: * call f + * proc=-1 normal call but no return * proc=0 normal call * proc=1 goroutine run in new proc * proc=2 defer call save away stack + * proc=3 normal call to C pointer (not Go func value) */ void ginscall(Node *f, int proc) { Prog *p; - Node reg, con; + Node reg, r1, con; switch(proc) { default: @@ -106,8 +124,25 @@ ginscall(Node *f, int proc) break; case 0: // normal call - p = gins(ACALL, N, f); - afunclit(&p->to); + case -1: // normal call but no return + if(f->op == ONAME && f->class == PFUNC) { + p = gins(ACALL, N, f); + afunclit(&p->to, f); + if(proc == -1 || noreturn(p)) + gins(AUNDEF, N, N); + break; + } + nodreg(®, types[tptr], D_DX); + nodreg(&r1, types[tptr], D_BX); + gmove(f, ®); + reg.op = OINDREG; + gmove(®, &r1); + reg.op = OREGISTER; + gins(ACALL, ®, &r1); + break; + + case 3: // normal call of c function pointer + gins(ACALL, N, f); break; case 1: // call in new proc (go) @@ -125,7 +160,7 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTL, ®, ®); - patch(gbranch(AJNE, T), retpc); + patch(gbranch(AJNE, T, -1), retpc); } break; } @@ -139,7 +174,7 @@ void cgen_callinter(Node *n, Node *res, int proc) { Node *i, *f; - Node tmpi, nodo, nodr, nodsp; + Node tmpi, nodi, nodo, nodr, nodsp; i = n->left; if(i->op != ODOTINTER) @@ -159,25 +194,35 @@ cgen_callinter(Node *n, Node *res, int proc) genlist(n->list); // assign the args - // Can regalloc now; i is known to be addable, - // so the agen will be easy. - regalloc(&nodr, types[tptr], res); - regalloc(&nodo, types[tptr], &nodr); - nodo.op = OINDREG; - - agen(i, &nodr); // REG = &inter + // i is now addable, prepare an indirected + // register to hold its address. + igen(i, &nodi, res); // REG = &inter nodindreg(&nodsp, types[tptr], D_SP); - nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 4(REG) -- i.data + nodi.type = types[tptr]; + nodi.xoffset += widthptr; + cgen(&nodi, &nodsp); // 0(SP) = 4(REG) -- i.data - nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab + regalloc(&nodo, types[tptr], res); + nodi.type = types[tptr]; + nodi.xoffset -= widthptr; + cgen(&nodi, &nodo); // REG = 0(REG) -- i.tab + regfree(&nodi); + regalloc(&nodr, types[tptr], &nodo); if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); + nodo.op = OINDREG; nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + + if(proc == 0) { + // plain call: use direct c function pointer - more efficient + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] + proc = 3; + } else { + // go/defer. generate go func value. + gins(ALEAL, &nodo, &nodr); // REG = &(20+offset(REG)) -- i.tab->fun[f] + } // BOTCH nodr.type = fntype; nodr.type = n->left->type; @@ -486,7 +531,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) int check; Node n1, t1, t2, t3, t4, n4, nz; Type *t, *t0; - Prog *p1, *p2, *p3; + Prog *p1, *p2; // Have to be careful about handling // most negative int divided by -1 correctly. @@ -535,23 +580,22 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) regalloc(&n1, t, N); gmove(&t2, &n1); gmove(&t1, ax); - p3 = P; + p2 = P; if(check) { nodconst(&n4, t, -1); gins(optoas(OCMP, t), &n1, &n4); - p1 = gbranch(optoas(ONE, t), T); - nodconst(&n4, t, -1LL<<(t->width*8-1)); - gins(optoas(OCMP, t), ax, &n4); - p2 = gbranch(optoas(ONE, t), T); - if(op == ODIV) - gmove(&n4, res); - if(op == OMOD) { + p1 = gbranch(optoas(ONE, t), T, +1); + if(op == ODIV) { + // a / (-1) is -a. + gins(optoas(OMINUS, t), N, ax); + gmove(ax, res); + } else { + // a % (-1) is 0. nodconst(&n4, t, 0); gmove(&n4, res); } - p3 = gbranch(AJMP, T); + p2 = gbranch(AJMP, T, 0); patch(p1, pc); - patch(p2, pc); } if(!issigned[t->etype]) { nodconst(&nz, t, 0); @@ -566,7 +610,7 @@ dodiv(int op, Node *nl, Node *nr, Node *res, Node *ax, Node *dx) else gmove(dx, res); if(check) - patch(p3, pc); + patch(p2, pc); } static void @@ -630,7 +674,7 @@ cgen_div(int op, Node *nl, Node *nr, Node *res) * res = nl >> nr */ void -cgen_shift(int op, Node *nl, Node *nr, Node *res) +cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res) { Node n1, n2, nt, cx, oldcx, hi, lo; int a, w; @@ -651,7 +695,7 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) gmove(&n2, &n1); sc = mpgetfix(nr->val.u.xval); if(sc >= nl->type->width*8) { - // large shift gets 2 shifts by width + // large shift gets 2 shifts by width-1 gins(a, ncon(w-1), &n1); gins(a, ncon(w-1), &n1); } else @@ -689,27 +733,39 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) } // test and fix up large shifts - if(nr->type->width > 4) { - // delayed reg alloc - nodreg(&n1, types[TUINT32], D_CX); - regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX - split64(&nt, &lo, &hi); - gmove(&lo, &n1); - gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0)); - p2 = gbranch(optoas(ONE, types[TUINT32]), T); - gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); - patch(p2, pc); - } else { - gins(optoas(OCMP, nr->type), &n1, ncon(w)); - p1 = gbranch(optoas(OLT, types[TUINT32]), T); - } - if(op == ORSH && issigned[nl->type->etype]) { - gins(a, ncon(w-1), &n2); + if(bounded) { + if(nr->type->width > 4) { + // delayed reg alloc + nodreg(&n1, types[TUINT32], D_CX); + regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX + split64(&nt, &lo, &hi); + gmove(&lo, &n1); + splitclean(); + } } else { - gmove(ncon(0), &n2); + if(nr->type->width > 4) { + // delayed reg alloc + nodreg(&n1, types[TUINT32], D_CX); + regalloc(&n1, types[TUINT32], &n1); // to hold the shift type in CX + split64(&nt, &lo, &hi); + gmove(&lo, &n1); + gins(optoas(OCMP, types[TUINT32]), &hi, ncon(0)); + p2 = gbranch(optoas(ONE, types[TUINT32]), T, +1); + gins(optoas(OCMP, types[TUINT32]), &n1, ncon(w)); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); + splitclean(); + patch(p2, pc); + } else { + gins(optoas(OCMP, nr->type), &n1, ncon(w)); + p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1); + } + if(op == ORSH && issigned[nl->type->etype]) { + gins(a, ncon(w-1), &n2); + } else { + gmove(ncon(0), &n2); + } + patch(p1, pc); } - patch(p1, pc); gins(a, &n1, &n2); if(oldcx.op != 0) @@ -724,444 +780,361 @@ cgen_shift(int op, Node *nl, Node *nr, Node *res) /* * generate byte multiply: * res = nl * nr - * no byte multiply instruction so have to do - * 16-bit multiply and take bottom half. + * there is no 2-operand byte multiply instruction so + * we do a full-width multiplication and truncate afterwards. */ void cgen_bmul(int op, Node *nl, Node *nr, Node *res) { - Node n1b, n2b, n1w, n2w; + Node n1, n2, nt, *tmp; Type *t; int a; - if(nl->ullman >= nr->ullman) { - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - } else { - regalloc(&n2b, nr->type, N); - cgen(nr, &n2b); - regalloc(&n1b, nl->type, res); - cgen(nl, &n1b); - } - - // copy from byte to short registers - t = types[TUINT16]; + // copy from byte to full registers + t = types[TUINT32]; if(issigned[nl->type->etype]) - t = types[TINT16]; - - regalloc(&n2w, t, &n2b); - cgen(&n2b, &n2w); + t = types[TINT32]; - regalloc(&n1w, t, &n1b); - cgen(&n1b, &n1w); + // largest ullman on left. + if(nl->ullman < nr->ullman) { + tmp = nl; + nl = nr; + nr = tmp; + } + tempname(&nt, nl->type); + cgen(nl, &nt); + regalloc(&n1, t, res); + cgen(nr, &n1); + regalloc(&n2, t, N); + gmove(&nt, &n2); a = optoas(op, t); - gins(a, &n2w, &n1w); - cgen(&n1w, &n1b); - cgen(&n1b, res); - - regfree(&n1w); - regfree(&n2w); - regfree(&n1b); - regfree(&n2b); + gins(a, &n2, &n1); + regfree(&n2); + gmove(&n1, res); + regfree(&n1); } -static int -regcmp(const void *va, const void *vb) +/* + * generate high multiply: + * res = (nl*nr) >> width + */ +void +cgen_hmul(Node *nl, Node *nr, Node *res) { - Node *ra, *rb; - - ra = (Node*)va; - rb = (Node*)vb; - return ra->local - rb->local; -} + Type *t; + int a; + Node n1, n2, ax, dx; -static Prog* throwpc; + t = nl->type; + a = optoas(OHMUL, t); + // gen nl in n1. + tempname(&n1, t); + cgen(nl, &n1); + // gen nr in n2. + regalloc(&n2, t, res); + cgen(nr, &n2); + + // multiply. + nodreg(&ax, t, D_AX); + gmove(&n2, &ax); + gins(a, &n1, N); + regfree(&n2); -// We're only going to bother inlining if we can -// convert all the arguments to 32 bits safely. Can we? -static int -fix64(NodeList *nn, int n) -{ - NodeList *l; - Node *r; - int i; - - l = nn; - for(i=0; i<n; i++) { - r = l->n->right; - if(is64(r->type) && !smallintconst(r)) { - if(r->op == OCONV) - r = r->left; - if(is64(r->type)) - return 0; - } - l = l->next; + if(t->width == 1) { + // byte multiply behaves differently. + nodreg(&ax, t, D_AH); + nodreg(&dx, t, D_DL); + gmove(&ax, &dx); } - return 1; + nodreg(&dx, t, D_DX); + gmove(&dx, res); } +static void cgen_float387(Node *n, Node *res); +static void cgen_floatsse(Node *n, Node *res); + +/* + * generate floating-point operation. + */ void -getargs(NodeList *nn, Node *reg, int n) +cgen_float(Node *n, Node *res) { - NodeList *l; - Node *r; - int i; - - throwpc = nil; - - l = nn; - for(i=0; i<n; i++) { - r = l->n->right; - if(is64(r->type)) { - if(r->op == OCONV) - r = r->left; - else if(smallintconst(r)) - r->type = types[TUINT32]; - if(is64(r->type)) - fatal("getargs"); + Node *nl; + Node n1, n2; + Prog *p1, *p2, *p3; + + nl = n->left; + switch(n->op) { + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + p1 = gbranch(AJMP, T, 0); + p2 = pc; + gmove(nodbool(1), res); + p3 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n, 1, 0, p2); + gmove(nodbool(0), res); + patch(p3, pc); + return; + + case OPLUS: + cgen(nl, res); + return; + + case OCONV: + if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) { + cgen(nl, res); + return; } - if(!smallintconst(r) && !isslice(r->type)) { - if(i < 3) // AX CX DX - nodreg(reg+i, r->type, D_AX+i); - else - reg[i].op = OXXX; - regalloc(reg+i, r->type, reg+i); - cgen(r, reg+i); - } else - reg[i] = *r; - if(reg[i].local != 0) - yyerror("local used"); - reg[i].local = l->n->left->xoffset; - l = l->next; + + tempname(&n2, n->type); + mgen(nl, &n1, res); + gmove(&n1, &n2); + gmove(&n2, res); + mfree(&n1); + return; } - qsort((void*)reg, n, sizeof(*reg), regcmp); - for(i=0; i<n; i++) - reg[i].local = 0; + + if(use_sse) + cgen_floatsse(n, res); + else + cgen_float387(n, res); } -void -cmpandthrow(Node *nl, Node *nr) +// floating-point. 387 (not SSE2) +static void +cgen_float387(Node *n, Node *res) { - vlong cl; - Prog *p1; - int op; - Node *c, n1; - Type *t; + Node f0, f1; + Node *nl, *nr; - op = OLE; - if(smallintconst(nl)) { - cl = mpgetfix(nl->val.u.xval); - if(cl == 0) - return; - if(smallintconst(nr)) - return; - // put the constant on the right - op = brrev(op); - c = nl; - nl = nr; - nr = c; - } - - // Arguments are known not to be 64-bit, - // but they might be smaller than 32 bits. - // Check if we need to use a temporary. - // At least one of the arguments is 32 bits - // (the len or cap) so one temporary suffices. - n1.op = OXXX; - t = types[TUINT32]; - if(nl->type->width != t->width) { - regalloc(&n1, t, nl); - gmove(nl, &n1); - nl = &n1; - } else if(nr->type->width != t->width) { - regalloc(&n1, t, nr); - gmove(nr, &n1); - nr = &n1; - } - gins(optoas(OCMP, t), nl, nr); - if(n1.op != OXXX) - regfree(&n1); - if(throwpc == nil) { - p1 = gbranch(optoas(op, t), T); - throwpc = pc; - ginscall(panicslice, 0); - patch(p1, pc); + nl = n->left; + nr = n->right; + nodreg(&f0, nl->type, D_F0); + nodreg(&f1, n->type, D_F0+1); + if(nr != N) + goto flt2; + + // unary + cgen(nl, &f0); + if(n->op != OCONV && n->op != OPLUS) + gins(foptoas(n->op, n->type, 0), N, N); + gmove(&f0, res); + return; + +flt2: // binary + if(nl->ullman >= nr->ullman) { + cgen(nl, &f0); + if(nr->addable) + gins(foptoas(n->op, n->type, 0), nr, &f0); + else { + cgen(nr, &f0); + gins(foptoas(n->op, n->type, Fpop), &f0, &f1); + } } else { - op = brcom(op); - p1 = gbranch(optoas(op, t), T); - patch(p1, throwpc); + cgen(nr, &f0); + if(nl->addable) + gins(foptoas(n->op, n->type, Frev), nl, &f0); + else { + cgen(nl, &f0); + gins(foptoas(n->op, n->type, Frev|Fpop), &f0, &f1); + } } -} + gmove(&f0, res); + return; -int -sleasy(Node *n) -{ - if(n->op != ONAME) - return 0; - if(!n->addable) - return 0; - return 1; } -// generate inline code for -// slicearray -// sliceslice -// arraytoslice -int -cgen_inline(Node *n, Node *res) +static void +cgen_floatsse(Node *n, Node *res) { - Node nodes[5]; - Node n1, n2, nres, ntemp; - vlong v; - int i, narg, nochk; - - if(n->op != OCALLFUNC) - goto no; - if(!n->left->addable) - goto no; - if(n->left->sym == S) - goto no; - if(n->left->sym->pkg != runtimepkg) - goto no; - if(strcmp(n->left->sym->name, "slicearray") == 0) - goto slicearray; - if(strcmp(n->left->sym->name, "sliceslice") == 0) { - narg = 4; - goto sliceslice; - } - if(strcmp(n->left->sym->name, "sliceslice1") == 0) { - narg = 3; - goto sliceslice; - } - goto no; - -slicearray: - if(!sleasy(res)) - goto no; - if(!fix64(n->list, 5)) - goto no; - getargs(n->list, nodes, 5); - - // if(hb[3] > nel[1]) goto throw - cmpandthrow(&nodes[3], &nodes[1]); - - // if(lb[2] > hb[3]) goto throw - cmpandthrow(&nodes[2], &nodes[3]); - - // len = hb[3] - lb[2] (destroys hb) - n2 = *res; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[3]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[3].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[3]); - gmove(&nodes[3], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } + Node *nl, *nr, *r; + Node n1, n2, nt; + int a; - // cap = nel[1] - lb[2] (destroys nel) - n2 = *res; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; + nl = n->left; + nr = n->right; + switch(n->op) { + default: + dump("cgen_floatsse", n); + fatal("cgen_floatsse %O", n->op); + return; - if(smallintconst(&nodes[1]) && smallintconst(&nodes[2])) { - v = mpgetfix(nodes[1].val.u.xval) - - mpgetfix(nodes[2].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[1]); - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[2]) || mpgetfix(nodes[2].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[2], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); + case OMINUS: + case OCOM: + nr = nodintconst(-1); + convlit(&nr, n->type); + a = foptoas(OMUL, nl->type, 0); + goto sbop; + + // symmetric binary + case OADD: + case OMUL: + a = foptoas(n->op, nl->type, 0); + goto sbop; + + // asymmetric binary + case OSUB: + case OMOD: + case ODIV: + a = foptoas(n->op, nl->type, 0); + goto abop; } - // if slice could be too big, dereference to - // catch nil array pointer. - if(nodes[0].op == OREGISTER && nodes[0].type->type->width >= unmappedzero) { - n2 = nodes[0]; - n2.xoffset = 0; - n2.op = OINDREG; - n2.type = types[TUINT8]; - gins(ATESTB, nodintconst(0), &n2); +sbop: // symmetric binary + if(nl->ullman < nr->ullman || nl->op == OLITERAL) { + r = nl; + nl = nr; + nr = r; } - // ary = old[0] + (lb[2] * width[4]) (destroys old) - n2 = *res; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[4])) { - v = mpgetfix(nodes[2].val.u.xval) * - mpgetfix(nodes[4].val.u.xval); - if(v != 0) { - nodconst(&n1, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); - } +abop: // asymmetric binary + if(nl->ullman >= nr->ullman) { + tempname(&nt, nl->type); + cgen(nl, &nt); + mgen(nr, &n2, N); + regalloc(&n1, nl->type, res); + gmove(&nt, &n1); + gins(a, &n2, &n1); + gmove(&n1, res); + regfree(&n1); + mfree(&n2); } else { - regalloc(&n1, types[tptr], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[4]) || mpgetfix(nodes[4].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[4], &n1); - gins(optoas(OADD, types[tptr]), &n1, &nodes[0]); + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + gins(a, &n2, &n1); + regfree(&n2); + gmove(&n1, res); regfree(&n1); } - gins(optoas(OAS, types[tptr]), &nodes[0], &n2); - - for(i=0; i<5; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); - } - return 1; + return; +} -sliceslice: - if(!fix64(n->list, narg)) - goto no; - nochk = n->etype; // skip bounds checking - ntemp.op = OXXX; - if(!sleasy(n->list->n->right)) { - Node *n0; - - n0 = n->list->n->right; - tempname(&ntemp, res->type); - cgen(n0, &ntemp); - n->list->n->right = &ntemp; - getargs(n->list, nodes, narg); - n->list->n->right = n0; - } else - getargs(n->list, nodes, narg); +void +bgen_float(Node *n, int true, int likely, Prog *to) +{ + int et, a; + Node *nl, *nr, *r; + Node n1, n2, n3, tmp, t1, t2, ax; + Prog *p1, *p2; - nres = *res; // result - if(!sleasy(res)) { - if(ntemp.op == OXXX) - tempname(&ntemp, res->type); - nres = ntemp; + nl = n->left; + nr = n->right; + a = n->op; + if(!true) { + // brcom is not valid on floats when NaN is involved. + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + // No need to avoid re-genning ninit. + bgen_float(n, 1, -likely, p2); + patch(gbranch(AJMP, T, 0), to); + patch(p2, pc); + return; } - if(narg == 3) { // old[lb:] - // move width to where it would be for old[lb:hb] - nodes[3] = nodes[2]; - nodes[2].op = OXXX; - - // if(lb[1] > old.nel[0]) goto throw; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - if(!nochk) - cmpandthrow(&nodes[1], &n2); - - // ret.nel = old.nel[0]-lb[1]; - n2 = nodes[0]; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], N); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } else { // old[lb:hb] - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - if (!nochk) { - // if(hb[2] > old.cap[0]) goto throw; - cmpandthrow(&nodes[2], &n2); - // if(lb[1] > hb[2]) goto throw; - cmpandthrow(&nodes[1], &nodes[2]); - } - - // ret.len = hb[2]-lb[1]; (destroys hb[2]) - n2 = nres; - n2.xoffset += Array_nel; - n2.type = types[TUINT32]; - - if(smallintconst(&nodes[2]) && smallintconst(&nodes[1])) { - v = mpgetfix(nodes[2].val.u.xval) - - mpgetfix(nodes[1].val.u.xval); - nodconst(&n1, types[TUINT32], v); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - } else { - regalloc(&n1, types[TUINT32], &nodes[2]); - gmove(&nodes[2], &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - } + if(use_sse) + goto sse; + else + goto x87; + +x87: + a = brrev(a); // because the args are stacked + if(a == OGE || a == OGT) { + // only < and <= work right with NaN; reverse if needed + r = nr; + nr = nl; + nl = r; + a = brrev(a); } - // ret.cap = old.cap[0]-lb[1]; (uses hb[2]) - n2 = nodes[0]; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - - regalloc(&n1, types[TUINT32], &nodes[2]); - gins(optoas(OAS, types[TUINT32]), &n2, &n1); - if(!smallintconst(&nodes[1]) || mpgetfix(nodes[1].val.u.xval) != 0) - gins(optoas(OSUB, types[TUINT32]), &nodes[1], &n1); - - n2 = nres; - n2.xoffset += Array_cap; - n2.type = types[TUINT32]; - gins(optoas(OAS, types[TUINT32]), &n1, &n2); - regfree(&n1); - - // ret.array = old.array[0]+lb[1]*width[3]; (uses lb[1]) - n2 = nodes[0]; - n2.xoffset += Array_array; - n2.type = types[tptr]; - - regalloc(&n1, types[tptr], &nodes[1]); - if(smallintconst(&nodes[1]) && smallintconst(&nodes[3])) { - gins(optoas(OAS, types[tptr]), &n2, &n1); - v = mpgetfix(nodes[1].val.u.xval) * - mpgetfix(nodes[3].val.u.xval); - if(v != 0) { - nodconst(&n2, types[tptr], v); - gins(optoas(OADD, types[tptr]), &n2, &n1); + nodreg(&tmp, nr->type, D_F0); + nodreg(&n2, nr->type, D_F0 + 1); + nodreg(&ax, types[TUINT16], D_AX); + et = simsimtype(nr->type); + if(et == TFLOAT64) { + if(nl->ullman > nr->ullman) { + cgen(nl, &tmp); + cgen(nr, &tmp); + gins(AFXCHD, &tmp, &n2); + } else { + cgen(nr, &tmp); + cgen(nl, &tmp); } + gins(AFUCOMIP, &tmp, &n2); + gins(AFMOVDP, &tmp, &tmp); // annoying pop but still better than STSW+SAHF } else { - gmove(&nodes[1], &n1); - if(!smallintconst(&nodes[3]) || mpgetfix(nodes[3].val.u.xval) != 1) - gins(optoas(OMUL, types[tptr]), &nodes[3], &n1); - gins(optoas(OADD, types[tptr]), &n2, &n1); + // TODO(rsc): The moves back and forth to memory + // here are for truncating the value to 32 bits. + // This handles 32-bit comparison but presumably + // all the other ops have the same problem. + // We need to figure out what the right general + // solution is, besides telling people to use float64. + tempname(&t1, types[TFLOAT32]); + tempname(&t2, types[TFLOAT32]); + cgen(nr, &t1); + cgen(nl, &t2); + gmove(&t2, &tmp); + gins(AFCOMFP, &t1, &tmp); + gins(AFSTSW, N, &ax); + gins(ASAHF, N, N); } - n2 = nres; - n2.xoffset += Array_array; - n2.type = types[tptr]; - gins(optoas(OAS, types[tptr]), &n1, &n2); - regfree(&n1); + goto ret; - for(i=0; i<4; i++) { - if(nodes[i].op == OREGISTER) - regfree(&nodes[i]); +sse: + if(!nl->addable) { + tempname(&n1, nl->type); + cgen(nl, &n1); + nl = &n1; + } + if(!nr->addable) { + tempname(&tmp, nr->type); + cgen(nr, &tmp); + nr = &tmp; + } + regalloc(&n2, nr->type, N); + gmove(nr, &n2); + nr = &n2; + + if(nl->op != OREGISTER) { + regalloc(&n3, nl->type, N); + gmove(nl, &n3); + nl = &n3; } - if(!sleasy(res)) { - cgen(&nres, res); + if(a == OGE || a == OGT) { + // only < and <= work right with NaN; reverse if needed + r = nr; + nr = nl; + nl = r; + a = brrev(a); } - return 1; -no: - return 0; + gins(foptoas(OCMP, nr->type, 0), nl, nr); + if(nl->op == OREGISTER) + regfree(nl); + regfree(nr); + +ret: + if(a == OEQ) { + // neither NE nor P + p1 = gbranch(AJNE, T, -likely); + p2 = gbranch(AJPS, T, -likely); + patch(gbranch(AJMP, T, 0), to); + patch(p1, pc); + patch(p2, pc); + } else if(a == ONE) { + // either NE or P + patch(gbranch(AJNE, T, likely), to); + patch(gbranch(AJPS, T, likely), to); + } else + patch(gbranch(optoas(a, nr->type), T, likely), to); + } diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index da0055cd9..39717d5b1 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -94,9 +94,9 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) switch(a->type) { case D_BRANCH: - if(a->branch == nil) + if(a->u.branch == nil) fatal("unpatched branch"); - a->offset = a->branch->loc; + a->offset = a->u.branch->loc; default: t |= T_TYPE; @@ -137,7 +137,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) if(t & T_SYM) /* implies sym */ Bputc(b, s); if(t & T_FCONST) { - ieeedtod(&e, a->dval); + ieeedtod(&e, a->u.dval); l = e; Bputc(b, l); Bputc(b, l>>8); @@ -151,7 +151,7 @@ zaddr(Biobuf *b, Addr *a, int s, int gotype) return; } if(t & T_SCONST) { - n = a->sval; + n = a->u.sval; for(i=0; i<NSNAME; i++) { Bputc(b, *n); n++; @@ -293,7 +293,7 @@ dsname(Sym *s, int off, char *t, int n) p->to.type = D_SCONST; p->to.index = D_NONE; - memmove(p->to.sval, t, n); + memmove(p->to.u.sval, t, n); return off + n; } @@ -373,13 +373,13 @@ gdatacomplex(Node *nam, Mpcplx *cval) p = gins(ADATA, nam, N); p->from.scale = w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->real); + p->to.u.dval = mpgetflt(&cval->real); p = gins(ADATA, nam, N); p->from.scale = w; p->from.offset += w; p->to.type = D_FCONST; - p->to.dval = mpgetflt(&cval->imag); + p->to.u.dval = mpgetflt(&cval->imag); } void diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 5e89af04a..c4c184bb9 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -105,16 +105,24 @@ dumpdata(void) /* * generate a branch. * t is ignored. + * likely values are for branch prediction: + * -1 unlikely + * 0 no opinion + * +1 likely */ Prog* -gbranch(int as, Type *t) +gbranch(int as, Type *t, int likely) { Prog *p; USED(t); p = prog(as); p->to.type = D_BRANCH; - p->to.branch = P; + p->to.u.branch = P; + if(likely != 0) { + p->from.type = D_CONST; + p->from.offset = likely > 0; + } return p; } @@ -126,7 +134,7 @@ patch(Prog *p, Prog *to) { if(p->to.type != D_BRANCH) fatal("patch: not a branch"); - p->to.branch = to; + p->to.u.branch = to; p->to.offset = to->loc; } @@ -137,8 +145,8 @@ unpatch(Prog *p) if(p->to.type != D_BRANCH) fatal("unpatch: not a branch"); - q = p->to.branch; - p->to.branch = P; + q = p->to.u.branch; + p->to.u.branch = P; p->to.offset = 0; return q; } @@ -166,44 +174,6 @@ newplist(void) } void -clearstk(void) -{ - Plist *pl; - Prog *p1, *p2; - Node sp, di, cx, con, ax; - - if(plast->firstpc->to.offset <= 0) - return; - - // reestablish context for inserting code - // at beginning of function. - pl = plast; - p1 = pl->firstpc; - p2 = p1->link; - pc = mal(sizeof(*pc)); - clearp(pc); - p1->link = pc; - - // zero stack frame - nodreg(&sp, types[tptr], D_SP); - nodreg(&di, types[tptr], D_DI); - nodreg(&cx, types[TUINT32], D_CX); - nodconst(&con, types[TUINT32], p1->to.offset / widthptr); - gins(ACLD, N, N); - gins(AMOVL, &sp, &di); - gins(AMOVL, &con, &cx); - nodconst(&con, types[TUINT32], 0); - nodreg(&ax, types[TUINT32], D_AX); - gins(AMOVL, &con, &ax); - gins(AREP, N, N); - gins(ASTOSL, N, N); - - // continue with original code. - gins(ANOP, N, N)->link = p2; - pc = P; -} - -void gused(Node *n) { gins(ANOP, n, N); // used @@ -214,22 +184,23 @@ gjmp(Prog *to) { Prog *p; - p = gbranch(AJMP, T); + p = gbranch(AJMP, T, 0); if(to != P) patch(p, to); return p; } void -ggloblnod(Node *nam, int32 width) +ggloblnod(Node *nam) { Prog *p; p = gins(AGLOBL, nam, N); p->lineno = nam->lineno; + p->from.gotype = ngotype(nam); p->to.sym = S; p->to.type = D_CONST; - p->to.offset = width; + p->to.offset = nam->type->width; if(nam->readonly) p->from.scale = RODATA; if(nam->type != T && !haspointers(nam->type)) @@ -237,7 +208,7 @@ ggloblnod(Node *nam, int32 width) } void -ggloblsym(Sym *s, int32 width, int dupok) +ggloblsym(Sym *s, int32 width, int dupok, int rodata) { Prog *p; @@ -249,8 +220,20 @@ ggloblsym(Sym *s, int32 width, int dupok) p->to.index = D_NONE; p->to.offset = width; if(dupok) - p->from.scale = DUPOK; - p->from.scale |= RODATA; + p->from.scale |= DUPOK; + if(rodata) + p->from.scale |= RODATA; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_EXTERN; + p->from.index = D_NONE; + p->from.sym = s; } int @@ -273,11 +256,12 @@ isfat(Type *t) * call afunclit to fix up the argument. */ void -afunclit(Addr *a) +afunclit(Addr *a, Node *n) { if(a->type == D_ADDR && a->index == D_EXTERN) { a->type = D_EXTERN; a->index = D_NONE; + a->sym = n->sym; } } @@ -572,6 +556,22 @@ optoas(int op, Type *t) a = AXORL; break; + case CASE(OLROT, TINT8): + case CASE(OLROT, TUINT8): + a = AROLB; + break; + + case CASE(OLROT, TINT16): + case CASE(OLROT, TUINT16): + a = AROLW; + break; + + case CASE(OLROT, TINT32): + case CASE(OLROT, TUINT32): + case CASE(OLROT, TPTR32): + a = AROLL; + break; + case CASE(OLSH, TINT8): case CASE(OLSH, TUINT8): a = ASHLB; @@ -613,22 +613,38 @@ optoas(int op, Type *t) a = ASARL; break; + case CASE(OHMUL, TINT8): case CASE(OMUL, TINT8): case CASE(OMUL, TUINT8): a = AIMULB; break; + case CASE(OHMUL, TINT16): case CASE(OMUL, TINT16): case CASE(OMUL, TUINT16): a = AIMULW; break; + case CASE(OHMUL, TINT32): case CASE(OMUL, TINT32): case CASE(OMUL, TUINT32): case CASE(OMUL, TPTR32): a = AIMULL; break; + case CASE(OHMUL, TUINT8): + a = AMULB; + break; + + case CASE(OHMUL, TUINT16): + a = AMULW; + break; + + case CASE(OHMUL, TUINT32): + case CASE(OHMUL, TPTR32): + a = AMULL; + break; + case CASE(ODIV, TINT8): case CASE(OMOD, TINT8): a = AIDIVB; @@ -676,10 +692,14 @@ optoas(int op, Type *t) int foptoas(int op, Type *t, int flg) { - int et; + int et, a; + a = AGOK; et = simtype[t->etype]; + if(use_sse) + goto sse; + // If we need Fpop, it means we're working on // two different floating-point registers, not memory. // There the instruction only has a float64 form. @@ -756,8 +776,65 @@ foptoas(int op, Type *t, int flg) fatal("foptoas %O %T %#x", op, t, flg); return 0; + +sse: + switch(CASE(op, et)) { + default: + fatal("foptoas-sse: no entry %O-%T", op, t); + break; + + case CASE(OCMP, TFLOAT32): + a = AUCOMISS; + break; + + case CASE(OCMP, TFLOAT64): + a = AUCOMISD; + break; + + case CASE(OAS, TFLOAT32): + a = AMOVSS; + break; + + case CASE(OAS, TFLOAT64): + a = AMOVSD; + break; + + case CASE(OADD, TFLOAT32): + a = AADDSS; + break; + + case CASE(OADD, TFLOAT64): + a = AADDSD; + break; + + case CASE(OSUB, TFLOAT32): + a = ASUBSS; + break; + + case CASE(OSUB, TFLOAT64): + a = ASUBSD; + break; + + case CASE(OMUL, TFLOAT32): + a = AMULSS; + break; + + case CASE(OMUL, TFLOAT64): + a = AMULSD; + break; + + case CASE(ODIV, TFLOAT32): + a = ADIVSS; + break; + + case CASE(ODIV, TFLOAT64): + a = ADIVSD; + break; + } + return a; } + static int resvd[] = { // D_DI, // for movstring @@ -779,7 +856,9 @@ ginit(void) for(i=0; i<nelem(reg); i++) reg[i] = 1; - for(i=D_AL; i<=D_DI; i++) + for(i=D_AX; i<=D_DI; i++) + reg[i] = 0; + for(i=D_X0; i<=D_X7; i++) reg[i] = 0; for(i=0; i<nelem(resvd); i++) reg[resvd[i]]++; @@ -795,9 +874,12 @@ gclean(void) for(i=0; i<nelem(resvd); i++) reg[resvd[i]]--; - for(i=D_AL; i<=D_DI; i++) + for(i=D_AX; i<=D_DI; i++) if(reg[i]) yyerror("reg %R left allocated at %ux", i, regpc[i]); + for(i=D_X0; i<=D_X7; i++) + if(reg[i]) + yyerror("reg %R left allocated\n", i); } int32 @@ -805,7 +887,7 @@ anyregalloc(void) { int i, j; - for(i=D_AL; i<=D_DI; i++) { + for(i=D_AX; i<=D_DI; i++) { if(reg[i] == 0) goto ok; for(j=0; j<nelem(resvd); j++) @@ -814,6 +896,9 @@ anyregalloc(void) return 1; ok:; } + for(i=D_X0; i<=D_X7; i++) + if(reg[i]) + return 1; return 0; } @@ -832,14 +917,16 @@ regalloc(Node *n, Type *t, Node *o) et = simtype[t->etype]; switch(et) { + case TINT64: + case TUINT64: + fatal("regalloc64"); + case TINT8: case TUINT8: case TINT16: case TUINT16: case TINT32: case TUINT32: - case TINT64: - case TUINT64: case TPTR32: case TPTR64: case TBOOL: @@ -860,8 +947,22 @@ regalloc(Node *n, Type *t, Node *o) case TFLOAT32: case TFLOAT64: - i = D_F0; - goto out; + if(!use_sse) { + i = D_F0; + goto out; + } + if(o != N && o->op == OREGISTER) { + i = o->val.u.reg; + if(i >= D_X0 && i <= D_X7) + goto out; + } + for(i=D_X0; i<=D_X7; i++) + if(reg[i] == 0) + goto out; + fprint(2, "registers allocated at\n"); + for(i=D_X0; i<=D_X7; i++) + fprint(2, "\t%R\t%#lux\n", i, regpc[i]); + fatal("out of floating registers"); } yyerror("regalloc: unknown type %T", t); @@ -1069,9 +1170,9 @@ split64(Node *n, Node *lo, Node *hi) if(!is64(n->type)) fatal("split64 %T", n->type); - sclean[nsclean].op = OEMPTY; if(nsclean >= nelem(sclean)) fatal("split64 clean"); + sclean[nsclean].op = OEMPTY; nsclean++; switch(n->op) { default: @@ -1165,13 +1266,16 @@ memname(Node *n, Type *t) n->orig->sym = n->sym; } +static void floatmove(Node *f, Node *t); +static void floatmove_387(Node *f, Node *t); +static void floatmove_sse(Node *f, Node *t); + void gmove(Node *f, Node *t) { int a, ft, tt; Type *cvt; - Node r1, r2, t1, t2, flo, fhi, tlo, thi, con, f0, f1, ax, dx, cx; - Prog *p1, *p2, *p3; + Node r1, r2, flo, fhi, tlo, thi, con; if(debug['M']) print("gmove %N -> %N\n", f, t); @@ -1179,11 +1283,15 @@ gmove(Node *f, Node *t) ft = simsimtype(f->type); tt = simsimtype(t->type); cvt = t->type; - + if(iscomplex[ft] || iscomplex[tt]) { complexmove(f, t); return; } + if(isfloat[ft] || isfloat[tt]) { + floatmove(f, t); + return; + } // cannot have two integer memory operands; // except 64-bit, which always copies via registers anyway. @@ -1192,19 +1300,9 @@ gmove(Node *f, Node *t) // convert constant to desired type if(f->op == OLITERAL) { - if(tt == TFLOAT32) - convconst(&con, types[TFLOAT64], &f->val); - else - convconst(&con, t->type, &f->val); + convconst(&con, t->type, &f->val); f = &con; ft = simsimtype(con.type); - - // some constants can't move directly to memory. - if(ismem(t)) { - // float constants come from memory. - if(isfloat[tt]) - goto hard; - } } // value -> value copy, only one memory operand. @@ -1380,6 +1478,272 @@ gmove(Node *f, Node *t) gins(AMOVL, ncon(0), &thi); splitclean(); return; + } + + gins(a, f, t); + return; + +rsrc: + // requires register source + regalloc(&r1, f->type, t); + gmove(f, &r1); + gins(a, &r1, t); + regfree(&r1); + return; + +rdst: + // requires register destination + regalloc(&r1, t->type, t); + gins(a, f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +hard: + // requires register intermediate + regalloc(&r1, cvt, t); + gmove(f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +fatal: + // should not happen + fatal("gmove %N -> %N", f, t); +} + +static void +floatmove(Node *f, Node *t) +{ + Node r1, r2, t1, t2, tlo, thi, con, f0, f1, ax, dx, cx; + Type *cvt; + int ft, tt; + Prog *p1, *p2, *p3; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + cvt = t->type; + + // cannot have two floating point memory operands. + if(isfloat[ft] && isfloat[tt] && ismem(f) && ismem(t)) + goto hard; + + // convert constant to desired type + if(f->op == OLITERAL) { + convconst(&con, t->type, &f->val); + f = &con; + ft = simsimtype(con.type); + + // some constants can't move directly to memory. + if(ismem(t)) { + // float constants come from memory. + if(isfloat[tt]) + goto hard; + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch(CASE(ft, tt)) { + default: + if(use_sse) + floatmove_sse(f, t); + else + floatmove_387(f, t); + return; + + // float to very long integer. + case CASE(TFLOAT32, TINT64): + case CASE(TFLOAT64, TINT64): + if(f->op == OREGISTER) { + cvt = f->type; + goto hardmem; + } + nodreg(&r1, types[ft], D_F0); + if(ft == TFLOAT32) + gins(AFMOVF, f, &r1); + else + gins(AFMOVD, f, &r1); + + // set round to zero mode during conversion + memname(&t1, types[TUINT16]); + memname(&t2, types[TUINT16]); + gins(AFSTCW, N, &t1); + gins(AMOVW, ncon(0xf7f), &t2); + gins(AFLDCW, &t2, N); + if(tt == TINT16) + gins(AFMOVWP, &r1, t); + else if(tt == TINT32) + gins(AFMOVLP, &r1, t); + else + gins(AFMOVVP, &r1, t); + gins(AFLDCW, &t1, N); + return; + + case CASE(TFLOAT32, TUINT64): + case CASE(TFLOAT64, TUINT64): + if(!ismem(f)) { + cvt = f->type; + goto hardmem; + } + bignodes(); + nodreg(&f0, types[ft], D_F0); + nodreg(&f1, types[ft], D_F0 + 1); + nodreg(&ax, types[TUINT16], D_AX); + + if(ft == TFLOAT32) + gins(AFMOVF, f, &f0); + else + gins(AFMOVD, f, &f0); + + // if 0 > v { answer = 0 } + gins(AFMOVD, &zerof, &f0); + gins(AFUCOMIP, &f0, &f1); + p1 = gbranch(optoas(OGT, types[tt]), T, 0); + // if 1<<64 <= v { answer = 0 too } + gins(AFMOVD, &two64f, &f0); + gins(AFUCOMIP, &f0, &f1); + p2 = gbranch(optoas(OGT, types[tt]), T, 0); + patch(p1, pc); + gins(AFMOVVP, &f0, t); // don't care about t, but will pop the stack + split64(t, &tlo, &thi); + gins(AMOVL, ncon(0), &tlo); + gins(AMOVL, ncon(0), &thi); + splitclean(); + p1 = gbranch(AJMP, T, 0); + patch(p2, pc); + + // in range; algorithm is: + // if small enough, use native float64 -> int64 conversion. + // otherwise, subtract 2^63, convert, and add it back. + + // set round to zero mode during conversion + memname(&t1, types[TUINT16]); + memname(&t2, types[TUINT16]); + gins(AFSTCW, N, &t1); + gins(AMOVW, ncon(0xf7f), &t2); + gins(AFLDCW, &t2, N); + + // actual work + gins(AFMOVD, &two63f, &f0); + gins(AFUCOMIP, &f0, &f1); + p2 = gbranch(optoas(OLE, types[tt]), T, 0); + gins(AFMOVVP, &f0, t); + p3 = gbranch(AJMP, T, 0); + patch(p2, pc); + gins(AFMOVD, &two63f, &f0); + gins(AFSUBDP, &f0, &f1); + gins(AFMOVVP, &f0, t); + split64(t, &tlo, &thi); + gins(AXORL, ncon(0x80000000), &thi); // + 2^63 + patch(p3, pc); + splitclean(); + // restore rounding mode + gins(AFLDCW, &t1, N); + + patch(p1, pc); + return; + + /* + * integer to float + */ + case CASE(TINT64, TFLOAT32): + case CASE(TINT64, TFLOAT64): + if(t->op == OREGISTER) + goto hardmem; + nodreg(&f0, t->type, D_F0); + gins(AFMOVV, f, &f0); + if(tt == TFLOAT32) + gins(AFMOVFP, &f0, t); + else + gins(AFMOVDP, &f0, t); + return; + + case CASE(TUINT64, TFLOAT32): + case CASE(TUINT64, TFLOAT64): + // algorithm is: + // if small enough, use native int64 -> float64 conversion. + // otherwise, halve (rounding to odd?), convert, and double. + nodreg(&ax, types[TUINT32], D_AX); + nodreg(&dx, types[TUINT32], D_DX); + nodreg(&cx, types[TUINT32], D_CX); + tempname(&t1, f->type); + split64(&t1, &tlo, &thi); + gmove(f, &t1); + gins(ACMPL, &thi, ncon(0)); + p1 = gbranch(AJLT, T, 0); + // native + t1.type = types[TINT64]; + nodreg(&r1, types[tt], D_F0); + gins(AFMOVV, &t1, &r1); + if(tt == TFLOAT32) + gins(AFMOVFP, &r1, t); + else + gins(AFMOVDP, &r1, t); + p2 = gbranch(AJMP, T, 0); + // simulated + patch(p1, pc); + gmove(&tlo, &ax); + gmove(&thi, &dx); + p1 = gins(ASHRL, ncon(1), &ax); + p1->from.index = D_DX; // double-width shift DX -> AX + p1->from.scale = 0; + gins(AMOVL, ncon(0), &cx); + gins(ASETCC, N, &cx); + gins(AORL, &cx, &ax); + gins(ASHRL, ncon(1), &dx); + gmove(&dx, &thi); + gmove(&ax, &tlo); + nodreg(&r1, types[tt], D_F0); + nodreg(&r2, types[tt], D_F0 + 1); + gins(AFMOVV, &t1, &r1); + gins(AFMOVD, &r1, &r1); + gins(AFADDDP, &r1, &r2); + if(tt == TFLOAT32) + gins(AFMOVFP, &r1, t); + else + gins(AFMOVDP, &r1, t); + patch(p2, pc); + splitclean(); + return; + } + +hard: + // requires register intermediate + regalloc(&r1, cvt, t); + gmove(f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +hardmem: + // requires memory intermediate + tempname(&r1, cvt); + gmove(f, &r1); + gmove(&r1, t); + return; +} + +static void +floatmove_387(Node *f, Node *t) +{ + Node r1, t1, t2; + Type *cvt; + Prog *p1, *p2, *p3; + int a, ft, tt; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + cvt = t->type; + + switch(CASE(ft, tt)) { + default: + goto fatal; /* * float to integer @@ -1429,10 +1793,10 @@ gmove(Node *f, Node *t) fatal("gmove %T", t); case TINT8: gins(ACMPL, &t1, ncon(-0x80)); - p1 = gbranch(optoas(OLT, types[TINT32]), T); + p1 = gbranch(optoas(OLT, types[TINT32]), T, -1); gins(ACMPL, &t1, ncon(0x7f)); - p2 = gbranch(optoas(OGT, types[TINT32]), T); - p3 = gbranch(AJMP, T); + p2 = gbranch(optoas(OGT, types[TINT32]), T, -1); + p3 = gbranch(AJMP, T, 0); patch(p1, pc); patch(p2, pc); gmove(ncon(-0x80), &t1); @@ -1441,14 +1805,14 @@ gmove(Node *f, Node *t) break; case TUINT8: gins(ATESTL, ncon(0xffffff00), &t1); - p1 = gbranch(AJEQ, T); + p1 = gbranch(AJEQ, T, +1); gins(AMOVL, ncon(0), &t1); patch(p1, pc); gmove(&t1, t); break; case TUINT16: gins(ATESTL, ncon(0xffff0000), &t1); - p1 = gbranch(AJEQ, T); + p1 = gbranch(AJEQ, T, +1); gins(AMOVL, ncon(0), &t1); patch(p1, pc); gmove(&t1, t); @@ -1459,73 +1823,8 @@ gmove(Node *f, Node *t) case CASE(TFLOAT32, TUINT32): case CASE(TFLOAT64, TUINT32): // convert via int64. - tempname(&t1, types[TINT64]); - gmove(f, &t1); - split64(&t1, &tlo, &thi); - gins(ACMPL, &thi, ncon(0)); - p1 = gbranch(AJEQ, T); - gins(AMOVL, ncon(0), &tlo); - patch(p1, pc); - gmove(&tlo, t); - splitclean(); - return; - - case CASE(TFLOAT32, TUINT64): - case CASE(TFLOAT64, TUINT64): - bignodes(); - nodreg(&f0, types[ft], D_F0); - nodreg(&f1, types[ft], D_F0 + 1); - nodreg(&ax, types[TUINT16], D_AX); - - gmove(f, &f0); - - // if 0 > v { answer = 0 } - gmove(&zerof, &f0); - gins(AFUCOMIP, &f0, &f1); - p1 = gbranch(optoas(OGT, types[tt]), T); - // if 1<<64 <= v { answer = 0 too } - gmove(&two64f, &f0); - gins(AFUCOMIP, &f0, &f1); - p2 = gbranch(optoas(OGT, types[tt]), T); - patch(p1, pc); - gins(AFMOVVP, &f0, t); // don't care about t, but will pop the stack - split64(t, &tlo, &thi); - gins(AMOVL, ncon(0), &tlo); - gins(AMOVL, ncon(0), &thi); - splitclean(); - p1 = gbranch(AJMP, T); - patch(p2, pc); - - // in range; algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - - // set round to zero mode during conversion - memname(&t1, types[TUINT16]); - memname(&t2, types[TUINT16]); - gins(AFSTCW, N, &t1); - gins(AMOVW, ncon(0xf7f), &t2); - gins(AFLDCW, &t2, N); - - // actual work - gmove(&two63f, &f0); - gins(AFUCOMIP, &f0, &f1); - p2 = gbranch(optoas(OLE, types[tt]), T); - gins(AFMOVVP, &f0, t); - p3 = gbranch(AJMP, T); - patch(p2, pc); - gmove(&two63f, &f0); - gins(AFSUBDP, &f0, &f1); - gins(AFMOVVP, &f0, t); - split64(t, &tlo, &thi); - gins(AXORL, ncon(0x80000000), &thi); // + 2^63 - patch(p3, pc); - splitclean(); - // restore rounding mode - gins(AFLDCW, &t1, N); - - patch(p1, pc); - return; + cvt = types[TINT64]; + goto hardmem; /* * integer to float @@ -1571,45 +1870,6 @@ gmove(Node *f, Node *t) cvt = types[TINT64]; goto hardmem; - case CASE(TUINT64, TFLOAT32): - case CASE(TUINT64, TFLOAT64): - // algorithm is: - // if small enough, use native int64 -> uint64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - nodreg(&ax, types[TUINT32], D_AX); - nodreg(&dx, types[TUINT32], D_DX); - nodreg(&cx, types[TUINT32], D_CX); - tempname(&t1, f->type); - split64(&t1, &tlo, &thi); - gmove(f, &t1); - gins(ACMPL, &thi, ncon(0)); - p1 = gbranch(AJLT, T); - // native - t1.type = types[TINT64]; - gmove(&t1, t); - p2 = gbranch(AJMP, T); - // simulated - patch(p1, pc); - gmove(&tlo, &ax); - gmove(&thi, &dx); - p1 = gins(ASHRL, ncon(1), &ax); - p1->from.index = D_DX; // double-width shift DX -> AX - p1->from.scale = 0; - gins(ASETCC, N, &cx); - gins(AORB, &cx, &ax); - gins(ASHRL, ncon(1), &dx); - gmove(&dx, &thi); - gmove(&ax, &tlo); - nodreg(&r1, types[tt], D_F0); - nodreg(&r2, types[tt], D_F0 + 1); - gmove(&t1, &r1); // t1.type is TINT64 now, set above - gins(AFMOVD, &r1, &r1); - gins(AFADDDP, &r1, &r2); - gmove(&r1, t); - patch(p2, pc); - splitclean(); - return; - /* * float to float */ @@ -1673,20 +1933,121 @@ gmove(Node *f, Node *t) gins(a, f, t); return; -rsrc: - // requires register source - regalloc(&r1, f->type, t); +hard: + // requires register intermediate + regalloc(&r1, cvt, t); gmove(f, &r1); - gins(a, &r1, t); + gmove(&r1, t); regfree(&r1); return; -rdst: - // requires register destination - regalloc(&r1, t->type, t); - gins(a, f, &r1); +hardmem: + // requires memory intermediate + tempname(&r1, cvt); + gmove(f, &r1); gmove(&r1, t); - regfree(&r1); + return; + +fatal: + // should not happen + fatal("gmove %lN -> %lN", f, t); + return; +} + +static void +floatmove_sse(Node *f, Node *t) +{ + Node r1; + Type *cvt; + int a, ft, tt; + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + + switch(CASE(ft, tt)) { + default: + // should not happen + fatal("gmove %N -> %N", f, t); + return; + /* + * float to integer + */ + case CASE(TFLOAT32, TINT16): + case CASE(TFLOAT32, TINT8): + case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT64, TINT16): + case CASE(TFLOAT64, TINT8): + case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TUINT8): + // convert via int32. + cvt = types[TINT32]; + goto hard; + + case CASE(TFLOAT32, TUINT32): + case CASE(TFLOAT64, TUINT32): + // convert via int64. + cvt = types[TINT64]; + goto hardmem; + + case CASE(TFLOAT32, TINT32): + a = ACVTTSS2SL; + goto rdst; + + case CASE(TFLOAT64, TINT32): + a = ACVTTSD2SL; + goto rdst; + + /* + * integer to float + */ + case CASE(TINT8, TFLOAT32): + case CASE(TINT8, TFLOAT64): + case CASE(TINT16, TFLOAT32): + case CASE(TINT16, TFLOAT64): + case CASE(TUINT16, TFLOAT32): + case CASE(TUINT16, TFLOAT64): + case CASE(TUINT8, TFLOAT32): + case CASE(TUINT8, TFLOAT64): + // convert via int32 memory + cvt = types[TINT32]; + goto hard; + + case CASE(TUINT32, TFLOAT32): + case CASE(TUINT32, TFLOAT64): + // convert via int64 memory + cvt = types[TINT64]; + goto hardmem; + + case CASE(TINT32, TFLOAT32): + a = ACVTSL2SS; + goto rdst; + + case CASE(TINT32, TFLOAT64): + a = ACVTSL2SD; + goto rdst; + + /* + * float to float + */ + case CASE(TFLOAT32, TFLOAT32): + a = AMOVSS; + break; + + case CASE(TFLOAT64, TFLOAT64): + a = AMOVSD; + break; + + case CASE(TFLOAT32, TFLOAT64): + a = ACVTSS2SD; + goto rdst; + + case CASE(TFLOAT64, TFLOAT32): + a = ACVTSD2SS; + goto rdst; + } + + gins(a, f, t); return; hard: @@ -1704,9 +2065,13 @@ hardmem: gmove(&r1, t); return; -fatal: - // should not happen - fatal("gmove %N -> %N", f, t); +rdst: + // requires register destination + regalloc(&r1, t->type, t); + gins(a, f, &r1); + gmove(&r1, t); + regfree(&r1); + return; } int @@ -1737,6 +2102,10 @@ gins(int as, Node *f, Node *t) if(as == AFMOVF && f && f->op == OREGISTER && t && t->op == OREGISTER) fatal("gins MOVF reg, reg"); + if(as == ACVTSD2SS && f && f->op == OLITERAL) + fatal("gins CVTSD2SS const"); + if(as == AMOVSD && t && t->op == OREGISTER && t->val.u.reg == D_F0) + fatal("gins MOVSD into F0"); switch(as) { case AMOVB: @@ -1744,6 +2113,12 @@ gins(int as, Node *f, Node *t) case AMOVL: if(f != N && t != N && samaddr(f, t)) return nil; + break; + + case ALEAL: + if(f != N && isconst(f, CTNIL)) + fatal("gins LEAL nil %T", f->type); + break; } memset(&af, 0, sizeof af); @@ -1782,6 +2157,25 @@ gins(int as, Node *f, Node *t) return p; } +// Generate an instruction referencing *n +// to force segv on nil pointer dereference. +void +checkref(Node *n) +{ + Node m; + + if(n->type->type->width < unmappedzero) + return; + + regalloc(&m, types[TUINTPTR], n); + cgen(n, &m); + m.xoffset = 0; + m.op = OINDREG; + m.type = types[TUINT8]; + gins(ATESTB, nodintconst(0), &m); + regfree(&m); +} + static void checkoffset(Addr *a, int canemitcode) { @@ -1807,6 +2201,8 @@ checkoffset(Addr *a, int canemitcode) void naddr(Node *n, Addr *a, int canemitcode) { + Prog *p; + a->scale = 0; a->index = D_NONE; a->type = D_NONE; @@ -1842,13 +2238,29 @@ naddr(Node *n, Addr *a, int canemitcode) a->node = n->left->orig; break; + case OCLOSUREVAR: + if(!canemitcode) + fatal("naddr OCLOSUREVAR cannot emit code"); + p = gins(AMOVL, N, N); + p->from.type = D_DX+D_INDIR; + p->from.offset = n->xoffset; + p->to.type = D_BX; + a->type = D_BX; + a->sym = S; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = n->left->sym; + break; + case ONAME: a->etype = 0; a->width = 0; if(n->type != T) { a->etype = simtype[n->type->etype]; + dowidth(n->type); a->width = n->type->width; - a->gotype = ngotype(n); } a->offset = n->xoffset; a->sym = n->sym; @@ -1880,6 +2292,7 @@ naddr(Node *n, Addr *a, int canemitcode) case PFUNC: a->index = D_EXTERN; a->type = D_ADDR; + a->sym = funcsym(a->sym); break; } break; @@ -1891,7 +2304,7 @@ naddr(Node *n, Addr *a, int canemitcode) break; case CTFLT: a->type = D_FCONST; - a->dval = mpgetflt(n->val.u.fval); + a->u.dval = mpgetflt(n->val.u.fval); break; case CTINT: case CTRUNE: diff --git a/src/cmd/8g/list.c b/src/cmd/8g/list.c index 88d3d5f7e..ec02ba5c5 100644 --- a/src/cmd/8g/list.c +++ b/src/cmd/8g/list.c @@ -107,7 +107,7 @@ Dconv(Fmt *fp) break; case D_BRANCH: - snprint(str, sizeof(str), "%d", a->branch->loc); + snprint(str, sizeof(str), "%d", a->u.branch->loc); break; case D_EXTERN: @@ -137,11 +137,11 @@ Dconv(Fmt *fp) break; case D_FCONST: - snprint(str, sizeof(str), "$(%.17e)", a->dval); + snprint(str, sizeof(str), "$(%.17e)", a->u.dval); break; case D_SCONST: - snprint(str, sizeof(str), "$\"%Y\"", a->sval); + snprint(str, sizeof(str), "$\"%Y\"", a->u.sval); break; case D_ADDR: @@ -158,7 +158,10 @@ brk: strcat(str, s); } conv: - return fmtstrcpy(fp, str); + fmtstrcpy(fp, str); + if(a->gotype) + fmtprint(fp, "{%s}", a->gotype->name); + return 0; } static char* regstr[] = @@ -231,6 +234,15 @@ static char* regstr[] = "TR6", "TR7", + "X0", /* [D_X0] */ + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", + "NONE", /* [D_NONE] */ }; diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h index ed6eb15ab..b80043e0f 100644 --- a/src/cmd/8g/opt.h +++ b/src/cmd/8g/opt.h @@ -47,12 +47,16 @@ typedef struct Reg Reg; typedef struct Rgn Rgn; +// A Reg is a wrapper around a single Prog (one instruction) that holds +// register optimization information while the optimizer runs. +// r->prog is the instruction. +// r->prog->regp points back to r. struct Reg { - Bits set; - Bits use1; - Bits use2; + Bits set; // variables written by this instruction. + Bits use1; // variables read by prog->from. + Bits use2; // variables read by prog->to. Bits refbehind; Bits refahead; @@ -68,13 +72,13 @@ struct Reg uint16 loop; // x5 for every loop uchar refset; // diagnostic generated - Reg* p1; - Reg* p2; + Reg* p1; // predecessors of this instruction: p1, + Reg* p2; // and then p2 linked though p2link. Reg* p2link; - Reg* s1; + Reg* s1; // successors of this instruction (at most two: s1 and s2). Reg* s2; - Reg* link; - Prog* prog; + Reg* link; // next instruction in function code + Prog* prog; // actual instruction }; #define R ((Reg*)0) diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index b8a2825e5..e5a3149cf 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -36,6 +36,7 @@ #define REGEXT 0 static void conprop(Reg *r); +static void elimshortmov(Reg *r); // do we need the carry bit static int @@ -45,9 +46,15 @@ needc(Prog *p) switch(p->as) { case AADCL: case ASBBL: + case ARCRB: + case ARCRW: case ARCRL: return 1; + case AADDB: + case AADDW: case AADDL: + case ASUBB: + case ASUBW: case ASUBL: case AJMP: case ARET: @@ -119,28 +126,14 @@ peep(void) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: p = p->link; } } - - // movb elimination. - // movb is simulated by the linker - // when a register other than ax, bx, cx, dx - // is used, so rewrite to other instructions - // when possible. a movb into a register - // can smash the entire 32-bit register without - // causing any trouble. - for(r=firstr; r!=R; r=r->link) { - p = r->prog; - if(p->as == AMOVB && regtyp(&p->to)) { - // movb into register. - // from another register or constant can be movl. - if(regtyp(&p->from) || p->from.type == D_CONST) - p->as = AMOVL; - else - p->as = AMOVBLZX; - } - } + + // byte, word arithmetic elimination. + elimshortmov(r); // constant propagation // find MOV $con,R followed by @@ -158,6 +151,8 @@ peep(void) case AMOVB: case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(regtyp(&p->to)) if(p->from.type == D_CONST) conprop(r); @@ -173,9 +168,9 @@ loop1: for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { - case AMOVB: - case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(regtyp(&p->to)) if(regtyp(&p->from)) { if(copyprop(r)) { @@ -205,7 +200,6 @@ loop1: } break; - case AADDB: case AADDL: case AADDW: if(p->from.type != D_CONST || needc(p->link)) @@ -228,7 +222,6 @@ loop1: } break; - case ASUBB: case ASUBL: case ASUBW: if(p->from.type != D_CONST || needc(p->link)) @@ -254,6 +247,19 @@ loop1: } if(t) goto loop1; + + // MOVSD removal. + // We never use packed registers, so a MOVSD between registers + // can be replaced by MOVAPD, which moves the pair of float64s + // instead of just the lower one. We only use the lower one, but + // the processor can do better if we do moves using both. + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(p->as == AMOVSD) + if(regtyp(&p->from)) + if(regtyp(&p->to)) + p->as = AMOVAPD; + } } void @@ -312,9 +318,104 @@ regtyp(Adr *a) t = a->type; if(t >= D_AX && t <= D_DI) return 1; + if(t >= D_X0 && t <= D_X7) + return 1; return 0; } +// movb elimination. +// movb is simulated by the linker +// when a register other than ax, bx, cx, dx +// is used, so rewrite to other instructions +// when possible. a movb into a register +// can smash the entire 64-bit register without +// causing any trouble. +static void +elimshortmov(Reg *r) +{ + Prog *p; + + for(r=firstr; r!=R; r=r->link) { + p = r->prog; + if(regtyp(&p->to)) { + switch(p->as) { + case AINCB: + case AINCW: + p->as = AINCL; + break; + case ADECB: + case ADECW: + p->as = ADECL; + break; + case ANEGB: + case ANEGW: + p->as = ANEGL; + break; + case ANOTB: + case ANOTW: + p->as = ANOTL; + break; + } + if(regtyp(&p->from) || p->from.type == D_CONST) { + // move or artihmetic into partial register. + // from another register or constant can be movl. + // we don't switch to 32-bit arithmetic if it can + // change how the carry bit is set (and the carry bit is needed). + switch(p->as) { + case AMOVB: + case AMOVW: + p->as = AMOVL; + break; + case AADDB: + case AADDW: + if(!needc(p->link)) + p->as = AADDL; + break; + case ASUBB: + case ASUBW: + if(!needc(p->link)) + p->as = ASUBL; + break; + case AMULB: + case AMULW: + p->as = AMULL; + break; + case AIMULB: + case AIMULW: + p->as = AIMULL; + break; + case AANDB: + case AANDW: + p->as = AANDL; + break; + case AORB: + case AORW: + p->as = AORL; + break; + case AXORB: + case AXORW: + p->as = AXORL; + break; + case ASHLB: + case ASHLW: + p->as = ASHLL; + break; + } + } else { + // explicit zero extension + switch(p->as) { + case AMOVB: + p->as = AMOVBLZX; + break; + case AMOVW: + p->as = AMOVWLZX; + break; + } + } + } + } +} + /* * the idea is to substitute * one register for another @@ -357,17 +458,6 @@ subprop(Reg *r0) if(p->to.type != D_NONE) break; - case ADIVB: - case ADIVL: - case ADIVW: - case AIDIVB: - case AIDIVL: - case AIDIVW: - case AIMULB: - case AMULB: - case AMULL: - case AMULW: - case ARCLB: case ARCLL: case ARCLW: @@ -392,6 +482,19 @@ subprop(Reg *r0) case ASHRB: case ASHRL: case ASHRW: + if(p->from.type == D_CONST) + break; + + case ADIVB: + case ADIVL: + case ADIVW: + case AIDIVB: + case AIDIVL: + case AIDIVW: + case AIMULB: + case AMULB: + case AMULL: + case AMULW: case AREP: case AREPN: @@ -403,11 +506,16 @@ subprop(Reg *r0) case ASTOSL: case AMOVSB: case AMOVSL: + + case AFMOVF: + case AFMOVD: + case AFMOVFP: + case AFMOVDP: return 0; - case AMOVB: - case AMOVW: case AMOVL: + case AMOVSS: + case AMOVSD: if(p->to.type == v1->type) goto gotit; break; @@ -587,13 +695,22 @@ copyu(Prog *p, Adr *v, Adr *s) case ANOP: /* rhs store */ - case AMOVB: - case AMOVW: case AMOVL: case AMOVBLSX: case AMOVBLZX: case AMOVWLSX: case AMOVWLZX: + + case AMOVSS: + case AMOVSD: + case ACVTSD2SL: + case ACVTSD2SS: + case ACVTSL2SD: + case ACVTSL2SS: + case ACVTSS2SD: + case ACVTSS2SL: + case ACVTTSD2SL: + case ACVTTSS2SL: if(copyas(&p->to, v)) { if(s != A) return copysub(&p->from, v, s, 1); @@ -653,6 +770,28 @@ copyu(Prog *p, Adr *v, Adr *s) case AXORB: case AXORL: case AXORW: + case AMOVB: + case AMOVW: + + case AADDSD: + case AADDSS: + case ACMPSD: + case ACMPSS: + case ADIVSD: + case ADIVSS: + case AMAXSD: + case AMAXSS: + case AMINSD: + case AMINSS: + case AMULSD: + case AMULSS: + case ARCPSS: + case ARSQRTSS: + case ASQRTSD: + case ASQRTSS: + case ASUBSD: + case ASUBSS: + case AXORPD: if(copyas(&p->to, v)) return 2; goto caseread; @@ -660,6 +799,11 @@ copyu(Prog *p, Adr *v, Adr *s) case ACMPL: /* read only */ case ACMPW: case ACMPB: + + case ACOMISD: + case ACOMISS: + case AUCOMISD: + case AUCOMISS: caseread: if(s != A) { if(copysub(&p->from, v, s, 1)) @@ -744,8 +888,6 @@ copyu(Prog *p, Adr *v, Adr *s) return 0; case ARET: /* funny */ - if(v->type == REGRET || v->type == FREGRET) - return 2; if(s != A) return 1; return 3; @@ -755,6 +897,8 @@ copyu(Prog *p, Adr *v, Adr *s) return 2; if(REGARG >= 0 && v->type == (uchar)REGARG) return 2; + if(v->type == p->from.type) + return 2; if(s != A) { if(copysub(&p->to, v, s, 1)) @@ -820,7 +964,7 @@ copysub(Adr *a, Adr *v, Adr *s, int f) if(copyas(a, v)) { t = s->type; - if(t >= D_AX && t <= D_DI) { + if(t >= D_AX && t <= D_DI || t >= D_X0 && t <= D_X7) { if(f) a->type = t; } @@ -881,7 +1025,7 @@ loop: if(p->from.node == p0->from.node) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) - if(p->from.dval == p0->from.dval) + if(p->from.u.vval == p0->from.u.vval) if(p->from.index == p0->from.index) { excise(r); goto loop; diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 45ffdf96c..985f6ccbc 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -33,13 +33,14 @@ #include "gg.h" #include "opt.h" -#define NREGVAR 8 -#define REGBITS ((uint32)0xff) +#define NREGVAR 16 /* 8 integer + 8 floating */ +#define REGBITS ((uint32)0xffff) #define P2R(p) (Reg*)(p->reg) static int first = 1; static void fixjmp(Prog*); +static void fixtemp(Prog*); Reg* rega(void) @@ -118,7 +119,12 @@ setaddrs(Bits bit) } } -static char* regname[] = { ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di" }; +static char* regname[] = { + ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di", + ".x0", ".x1", ".x2", ".x3", ".x4", ".x5", ".x6", ".x7", +}; + +static Node* regnodes[NREGVAR]; void regopt(Prog *firstp) @@ -135,6 +141,7 @@ regopt(Prog *firstp) first = 0; } + fixtemp(firstp); fixjmp(firstp); // count instructions @@ -147,7 +154,6 @@ regopt(Prog *firstp) return; } - r1 = R; firstr = R; lastr = R; @@ -158,8 +164,11 @@ regopt(Prog *firstp) */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) - var[i].node = newname(lookup(regname[i])); + for(i=0; i<NREGVAR; i++) { + if(regnodes[i] == N) + regnodes[i] = newname(lookup(regname[i])); + var[i].node = regnodes[i]; + } regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { @@ -186,6 +195,8 @@ regopt(Prog *firstp) case AGLOBL: case ANAME: case ASIGNAME: + case ALOCALS: + case ATYPE: continue; } r = rega(); @@ -213,6 +224,20 @@ regopt(Prog *firstp) } } + // Avoid making variables for direct-called functions. + if(p->as == ACALL && p->to.type == D_EXTERN) + continue; + + // Addressing makes some registers used. + if(p->from.type >= D_INDIR) + r->use1.b[0] |= RtoB(p->from.type-D_INDIR); + if(p->from.index != D_NONE) + r->use1.b[0] |= RtoB(p->from.index); + if(p->to.type >= D_INDIR) + r->use2.b[0] |= RtoB(p->to.type-D_INDIR); + if(p->to.index != D_NONE) + r->use2.b[0] |= RtoB(p->to.index); + bit = mkvar(r, &p->from); if(bany(&bit)) switch(p->as) { @@ -220,6 +245,8 @@ regopt(Prog *firstp) * funny */ case ALEAL: + case AFMOVD: + case AFMOVF: case AFMOVL: case AFMOVW: case AFMOVV: @@ -260,6 +287,10 @@ regopt(Prog *firstp) case ACMPB: case ACMPL: case ACMPW: + case ACOMISS: + case ACOMISD: + case AUCOMISS: + case AUCOMISD: case ATESTB: case ATESTL: case ATESTW: @@ -283,6 +314,17 @@ regopt(Prog *firstp) case AMOVWLSX: case AMOVWLZX: case APOPL: + + case AMOVSS: + case AMOVSD: + case ACVTSD2SL: + case ACVTSD2SS: + case ACVTSL2SD: + case ACVTSL2SS: + case ACVTSS2SD: + case ACVTSS2SL: + case ACVTTSD2SL: + case ACVTTSS2SL: for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; @@ -367,6 +409,26 @@ regopt(Prog *firstp) case AXCHGB: case AXCHGW: case AXCHGL: + + case AADDSD: + case AADDSS: + case ACMPSD: + case ACMPSS: + case ADIVSD: + case ADIVSS: + case AMAXSD: + case AMAXSS: + case AMINSD: + case AMINSS: + case AMULSD: + case AMULSS: + case ARCPSS: + case ARSQRTSS: + case ASQRTSD: + case ASQRTSS: + case ASUBSD: + case ASUBSS: + case AXORPD: for(z=0; z<BITS; z++) { r->set.b[z] |= bit.b[z]; r->use2.b[z] |= bit.b[z]; @@ -475,8 +537,9 @@ regopt(Prog *firstp) addrs.b[z] |= bit.b[z]; } -// print("bit=%2d addr=%d et=%-6E w=%-2d s=%S + %lld\n", -// i, v->addr, v->etype, v->width, v->sym, v->offset); + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); } if(debug['R'] && debug['v']) @@ -490,9 +553,9 @@ regopt(Prog *firstp) for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->to.type == D_BRANCH) { - if(p->to.branch == P) + if(p->to.u.branch == P) fatal("pnil %P", p); - r1 = p->to.branch->reg; + r1 = p->to.u.branch->reg; if(r1 == R) fatal("rnil %P", p); if(r1 == r) { @@ -673,8 +736,16 @@ brk: while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) - while(p->to.branch != P && p->to.branch->as == ANOP) - p->to.branch = p->to.branch->link; + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; + } + + if(!use_sse) + for(p=firstp; p!=P; p=p->link) { + if(p->from.type >= D_X0 && p->from.type <= D_X7) + fatal("invalid use of %R with GO386=387: %P", p->from.type, p); + if(p->to.type >= D_X0 && p->to.type <= D_X7) + fatal("invalid use of %R with GO386=387: %P", p->to.type, p); } if(lastr != R) { @@ -735,7 +806,6 @@ addmove(Reg *r, int bn, int rn, int f) a->offset = v->offset; a->etype = v->etype; a->type = v->name; - a->gotype = v->gotype; a->node = v->node; a->sym = v->node->sym; @@ -754,6 +824,12 @@ addmove(Reg *r, int bn, int rn, int f) case TUINT16: p1->as = AMOVW; break; + case TFLOAT32: + p1->as = AMOVSS; + break; + case TFLOAT64: + p1->as = AMOVSD; + break; case TINT: case TUINT: case TINT32: @@ -793,6 +869,9 @@ doregbits(int r) else if(r >= D_AH && r <= D_BH) b |= RtoB(r-D_AH+D_AX); + else + if(r >= D_X0 && r <= D_X0+7) + b |= FtoB(r); return b; } @@ -865,6 +944,8 @@ mkvar(Reg *r, Adr *a) et = a->etype; o = a->offset; w = a->width; + if(w < 0) + fatal("bad width %d for %D", w, a); flag = 0; for(i=0; i<nvar; i++) { @@ -902,14 +983,13 @@ mkvar(Reg *r, Adr *a) v = var+i; v->offset = o; v->name = n; - v->gotype = a->gotype; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) - print("bit=%2d et=%2d w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); ostats.nvar++; bit = blsh(i); @@ -972,8 +1052,12 @@ prop(Reg *r, Bits ref, Bits cal) default: // Work around for issue 1304: // flush modified globals before each instruction. - for(z=0; z<BITS; z++) + for(z=0; z<BITS; z++) { cal.b[z] |= externs.b[z]; + // issue 4066: flush modified return variables in case of panic + if(hasdefer) + cal.b[z] |= ovar.b[z]; + } break; } for(z=0; z<BITS; z++) { @@ -1190,6 +1274,13 @@ allreg(uint32 b, Rgn *r) case TFLOAT32: case TFLOAT64: + if(!use_sse) + break; + i = BtoF(~b); + if(i && r->cost > 0) { + r->regno = i; + return FtoB(i); + } break; } return 0; @@ -1279,7 +1370,7 @@ regset(Reg *r, uint32 bb) set = 0; v = zprog.from; while(b = bb & ~(bb-1)) { - v.type = BtoR(b); + v.type = b & 0xFF ? BtoR(b): BtoF(b); c = copyu(r->prog, &v, A); if(c == 3) set |= b; @@ -1298,7 +1389,7 @@ reguse(Reg *r, uint32 bb) set = 0; v = zprog.from; while(b = bb & ~(bb-1)) { - v.type = BtoR(b); + v.type = b & 0xFF ? BtoR(b): BtoF(b); c = copyu(r->prog, &v, A); if(c == 1 || c == 2 || c == 4) set |= b; @@ -1468,6 +1559,23 @@ BtoR(int32 b) return bitno(b) + D_AX; } +int32 +FtoB(int f) +{ + if(f < D_X0 || f > D_X7) + return 0; + return 1L << (f - D_X0 + 8); +} + +int +BtoF(int32 b) +{ + b &= 0xFF00L; + if(b == 0) + return 0; + return bitno(b) - 8 + D_X0; +} + void dumpone(Reg *r) { @@ -1581,7 +1689,7 @@ chasejmp(Prog *p, int *jmploop) *jmploop = 1; break; } - p = p->to.branch; + p = p->to.u.branch; } return p; } @@ -1603,9 +1711,9 @@ mark(Prog *firstp) if(p->reg != dead) break; p->reg = alive; - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch) - mark(p->to.branch); - if(p->as == AJMP || p->as == ARET || (p->as == ACALL && noreturn(p))) + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch) + mark(p->to.u.branch); + if(p->as == AJMP || p->as == ARET || p->as == AUNDEF) break; } } @@ -1624,8 +1732,8 @@ fixjmp(Prog *firstp) for(p=firstp; p; p=p->link) { if(debug['R'] && debug['v']) print("%P\n", p); - if(p->as != ACALL && p->to.type == D_BRANCH && p->to.branch && p->to.branch->as == AJMP) { - p->to.branch = chasejmp(p->to.branch, &jmploop); + if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) { + p->to.u.branch = chasejmp(p->to.u.branch, &jmploop); if(debug['R'] && debug['v']) print("->%P\n", p); } @@ -1661,7 +1769,7 @@ fixjmp(Prog *firstp) if(!jmploop) { last = nil; for(p=firstp; p; p=p->link) { - if(p->as == AJMP && p->to.type == D_BRANCH && p->to.branch == p->link) { + if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) { if(debug['R'] && debug['v']) print("del %P\n", p); continue; @@ -1680,3 +1788,131 @@ fixjmp(Prog *firstp) print("\n"); } } + +static uint32 +fnv1(Sym *sym) +{ + uint32 h; + char *s; + + h = 2166136261U; + for(s=sym->name;*s;s++) { + h = (16777619 * h) ^ (uint32)(uint8)(*s); + } + return h; +} + +static uint16 +hash32to16(uint32 h) +{ + return (h & 0xffff) ^ (h >> 16); +} + +/* + * fixtemp eliminates sequences like: + * MOV reg1, mem + * OP mem, reg2 + * when mem is a stack variable which is not mentioned + * anywhere else. The instructions are replaced by + * OP reg1, reg2 + * this reduces the number of variables that the register optimizer + * sees, which lets it do a better job and makes it less likely to turn + * itself off. + */ +static void +fixtemp(Prog *firstp) +{ + static uint8 counts[1<<16]; // A hash table to count variable occurences. + int i; + Prog *p, *p2; + uint32 h; + + if(debug['R'] && debug['v']) + print("\nfixtemp\n"); + + // Count variable references. We actually use a hashtable so this + // is only approximate. + for(i=0; i<nelem(counts); i++) + counts[i] = 0; + for(p=firstp; p!=P; p=p->link) { + if(p->from.type == D_AUTO) { + h = hash32to16(fnv1(p->from.sym)); + //print("seen %S hash %d\n", p->from.sym, hash32to16(h)); + if(counts[h] < 10) + counts[h]++; + } + if(p->to.type == D_AUTO) { + h = hash32to16(fnv1(p->to.sym)); + //print("seen %S hash %d\n", p->to.sym, hash32to16(h)); + if(counts[h] < 10) + counts[h]++; + } + } + + // Eliminate single-write, single-read stack variables. + for(p=firstp; p!=P; p=p->link) { + if(debug['R'] && debug['v']) + print("%P\n", p); + if(p->link == P || p->to.type != D_AUTO) + continue; + if(isfloat[p->to.etype] && FtoB(p->from.type)) { + switch(p->as) { + case AMOVSS: + case AMOVSD: + break; + default: + continue; + } + } else if(!isfloat[p->to.etype] && RtoB(p->from.type)) { + switch(p->as) { + case AMOVB: + if(p->to.width == 1) + break; + case AMOVW: + if(p->to.width == 2) + break; + case AMOVL: + if(p->to.width == 4) + break; + default: + continue; + } + } else + continue; + // p is a MOV reg, mem. + p2 = p->link; + h = hash32to16(fnv1(p->to.sym)); + if(counts[h] != 2) { + continue; + } + switch(p2->as) { + case ALEAL: + case AFMOVD: + case AFMOVF: + case AFMOVL: + case AFMOVW: + case AFMOVV: + // funny + continue; + } + // p2 is OP mem, reg2 + // and OP is not a funny instruction. + if(p2->from.sym == p->to.sym + && p2->from.offset == p->to.offset + && p2->from.type == p->to.type) { + if(debug['R'] && debug['v']) { + print(" ===elide== %D\n", &p->to); + print("%P", p2); + } + // p2 is OP mem, reg2. + // change to OP reg, reg2 and + // eliminate the mov. + p2->from = p->from; + *p = *p2; + p->link = p2->link; + if(debug['R'] && debug['v']) { + print(" ===change== %P\n", p); + } + } + } +} diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index 9d2751cf0..386889956 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -451,6 +451,125 @@ enum as ASFENCE, AEMMS, + + APREFETCHT0, + APREFETCHT1, + APREFETCHT2, + APREFETCHNTA, + + ABSWAPL, + + AUNDEF, + + // SSE2 + AADDPD, + AADDPS, + AADDSD, + AADDSS, + AANDNPD, + AANDNPS, + AANDPD, + AANDPS, + ACMPPD, + ACMPPS, + ACMPSD, + ACMPSS, + ACOMISD, + ACOMISS, + ACVTPL2PD, + ACVTPL2PS, + ACVTPD2PL, + ACVTPD2PS, + ACVTPS2PL, + ACVTPS2PD, + ACVTSD2SL, + ACVTSD2SS, + ACVTSL2SD, + ACVTSL2SS, + ACVTSS2SD, + ACVTSS2SL, + ACVTTPD2PL, + ACVTTPS2PL, + ACVTTSD2SL, + ACVTTSS2SL, + ADIVPD, + ADIVPS, + ADIVSD, + ADIVSS, + AMASKMOVOU, + AMAXPD, + AMAXPS, + AMAXSD, + AMAXSS, + AMINPD, + AMINPS, + AMINSD, + AMINSS, + AMOVAPD, + AMOVAPS, + AMOVO, + AMOVOU, + AMOVHLPS, + AMOVHPD, + AMOVHPS, + AMOVLHPS, + AMOVLPD, + AMOVLPS, + AMOVMSKPD, + AMOVMSKPS, + AMOVNTO, + AMOVNTPD, + AMOVNTPS, + AMOVSD, + AMOVSS, + AMOVUPD, + AMOVUPS, + AMULPD, + AMULPS, + AMULSD, + AMULSS, + AORPD, + AORPS, + APADDQ, + APMAXSW, + APMAXUB, + APMINSW, + APMINUB, + APSADBW, + APSUBB, + APSUBL, + APSUBQ, + APSUBSB, + APSUBSW, + APSUBUSB, + APSUBUSW, + APSUBW, + APUNPCKHQDQ, + APUNPCKLQDQ, + ARCPPS, + ARCPSS, + ARSQRTPS, + ARSQRTSS, + ASQRTPD, + ASQRTPS, + ASQRTSD, + ASQRTSS, + ASUBPD, + ASUBPS, + ASUBSD, + ASUBSS, + AUCOMISD, + AUCOMISS, + AUNPCKHPD, + AUNPCKHPS, + AUNPCKLPD, + AUNPCKLPS, + AXORPD, + AXORPS, + + AUSEFIELD, + ALOCALS, + ATYPE, ALAST }; @@ -496,17 +615,26 @@ enum D_DR = 43, D_TR = 51, - D_NONE = 59, - - D_BRANCH = 60, - D_EXTERN = 61, - D_STATIC = 62, - D_AUTO = 63, - D_PARAM = 64, - D_CONST = 65, - D_FCONST = 66, - D_SCONST = 67, - D_ADDR = 68, + D_X0 = 59, + D_X1, + D_X2, + D_X3, + D_X4, + D_X5, + D_X6, + D_X7, + + D_NONE = 67, + + D_BRANCH = 68, + D_EXTERN = 69, + D_STATIC = 70, + D_AUTO = 71, + D_PARAM = 72, + D_CONST = 73, + D_FCONST = 74, + D_SCONST = 75, + D_ADDR = 76, D_FILE, D_FILE1, @@ -538,7 +666,7 @@ enum /* * this is the ranlib header */ -#define SYMDEF "__.SYMDEF" +#define SYMDEF "__.GOSYMDEF" /* * this is the simulated IEEE floating point diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 25ffc786f..a00174c36 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -70,34 +70,6 @@ datoff(vlong addr) return 0; } -enum { - ElfStrEmpty, - ElfStrInterp, - ElfStrHash, - ElfStrGot, - ElfStrGotPlt, - ElfStrDynamic, - ElfStrDynsym, - ElfStrDynstr, - ElfStrRel, - ElfStrText, - ElfStrData, - ElfStrBss, - ElfStrShstrtab, - ElfStrSymtab, - ElfStrStrtab, - ElfStrRelPlt, - ElfStrPlt, - ElfStrGnuVersion, - ElfStrGnuVersionR, - ElfStrNoteNetbsdIdent, - ElfStrNoPtrData, - ElfStrNoPtrBss, - NElfStr -}; - -vlong elfstr[NElfStr]; - static int needlib(char *name) { @@ -123,6 +95,21 @@ int nelfsym = 1; static void addpltsym(Sym*); static void addgotsym(Sym*); +Sym * +lookuprel(void) +{ + return lookup(".rel", 0); +} + +void +adddynrela(Sym *rela, Sym *s, Reloc *r) +{ + USED(rela); + USED(s); + USED(r); + sysfatal("adddynrela not implemented"); +} + void adddynrel(Sym *s, Reloc *r) { @@ -282,7 +269,36 @@ adddynrel(Sym *s, Reloc *r) diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); } -static void +int +elfreloc1(Reloc *r, vlong off, int32 elfsym, vlong add) +{ + USED(add); // written to obj file by ../ld/data.c's reloc + + LPUT(off); + + switch(r->type) { + default: + return -1; + + case D_ADDR: + if(r->siz == 4) + LPUT(R_386_32 | elfsym<<8); + else + return -1; + break; + + case D_PCREL: + if(r->siz == 4) + LPUT(R_386_PC32 | elfsym<<8); + else + return -1; + break; + } + + return 0; +} + +void elfsetupplt(void) { Sym *plt, *got; @@ -415,6 +431,7 @@ adddynsym(Sym *s) Sym *d, *str; int t; char *name; + vlong off; if(s->dynid >= 0) return; @@ -444,7 +461,7 @@ adddynsym(Sym *s) /* type */ t = STB_GLOBAL << 4; - if(s->dynexport && s->type == STEXT) + if(s->dynexport && (s->type&SMASK) == STEXT) t |= STT_FUNC; else t |= STT_OBJECT; @@ -478,16 +495,51 @@ adddynsym(Sym *s) name = s->dynimpname; if(name == nil) name = s->name; - s->dynid = d->size/12; + if(d->size == 0 && ndynexp > 0) { // pre-allocate for dynexps + symgrow(d, ndynexp*12); + } + if(s->dynid <= -100) { // pre-allocated, see cmd/ld/go.c:^sortdynexp() + s->dynid = -s->dynid-100; + off = s->dynid*12; + } else { + off = d->size; + s->dynid = off/12; + } // darwin still puts _ prefixes on all C symbols str = lookup(".dynstr", 0); - adduint32(d, str->size); + setuint32(d, off, str->size); + off += 4; adduint8(str, '_'); addstring(str, name); - adduint8(d, 0x01); // type - N_EXT - external symbol - adduint8(d, 0); // section - adduint16(d, 0); // desc - adduint32(d, 0); // value + if(s->type == SDYNIMPORT) { + setuint8(d, off, 0x01); // type - N_EXT - external symbol + off++; + setuint8(d, off, 0); // section + off++; + } else { + setuint8(d, off, 0x0f); + off++; + switch(s->type) { + default: + case STEXT: + setuint8(d, off, 1); + break; + case SDATA: + setuint8(d, off, 2); + break; + case SBSS: + setuint8(d, off, 4); + break; + } + off++; + } + setuint16(d, off, 0); // desc + off += 2; + if(s->type == SDYNIMPORT) + setuint32(d, off, 0); // value + else + setaddr(d, off, s); + off += 4; } else if(HEADTYPE != Hwindows) { diag("adddynsym: unsupported binary format"); } @@ -514,177 +566,26 @@ adddynlib(char *lib) } void -doelf(void) -{ - Sym *s, *shstrtab, *dynstr; - - if(!iself) - return; - - /* predefine strings we need for section headers */ - shstrtab = lookup(".shstrtab", 0); - shstrtab->type = SELFROSECT; - shstrtab->reachable = 1; - - elfstr[ElfStrEmpty] = addstring(shstrtab, ""); - elfstr[ElfStrText] = addstring(shstrtab, ".text"); - elfstr[ElfStrNoPtrData] = addstring(shstrtab, ".noptrdata"); - elfstr[ElfStrData] = addstring(shstrtab, ".data"); - elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); - elfstr[ElfStrNoPtrBss] = addstring(shstrtab, ".noptrbss"); - if(HEADTYPE == Hnetbsd) - elfstr[ElfStrNoteNetbsdIdent] = addstring(shstrtab, ".note.netbsd.ident"); - addstring(shstrtab, ".elfdata"); - addstring(shstrtab, ".rodata"); - addstring(shstrtab, ".gosymtab"); - addstring(shstrtab, ".gopclntab"); - if(!debug['s']) { - elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); - elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); - dwarfaddshstrings(shstrtab); - } - elfstr[ElfStrShstrtab] = addstring(shstrtab, ".shstrtab"); - - if(!debug['d']) { /* -d suppresses dynamic loader format */ - elfstr[ElfStrInterp] = addstring(shstrtab, ".interp"); - elfstr[ElfStrHash] = addstring(shstrtab, ".hash"); - elfstr[ElfStrGot] = addstring(shstrtab, ".got"); - elfstr[ElfStrGotPlt] = addstring(shstrtab, ".got.plt"); - elfstr[ElfStrDynamic] = addstring(shstrtab, ".dynamic"); - elfstr[ElfStrDynsym] = addstring(shstrtab, ".dynsym"); - elfstr[ElfStrDynstr] = addstring(shstrtab, ".dynstr"); - elfstr[ElfStrRel] = addstring(shstrtab, ".rel"); - elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt"); - elfstr[ElfStrPlt] = addstring(shstrtab, ".plt"); - elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version"); - elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r"); - - /* dynamic symbol table - first entry all zeros */ - s = lookup(".dynsym", 0); - s->type = SELFROSECT; - s->reachable = 1; - s->size += ELF32SYMSIZE; - - /* dynamic string table */ - s = lookup(".dynstr", 0); - s->reachable = 1; - s->type = SELFROSECT; - if(s->size == 0) - addstring(s, ""); - dynstr = s; - - /* relocation table */ - s = lookup(".rel", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* global offset table */ - s = lookup(".got", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* hash */ - s = lookup(".hash", 0); - s->reachable = 1; - s->type = SELFROSECT; - - /* got.plt */ - s = lookup(".got.plt", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - s = lookup(".plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".rel.plt", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version", 0); - s->reachable = 1; - s->type = SELFROSECT; - - s = lookup(".gnu.version_r", 0); - s->reachable = 1; - s->type = SELFROSECT; - - elfsetupplt(); - - /* define dynamic elf table */ - s = lookup(".dynamic", 0); - s->reachable = 1; - s->type = SELFSECT; // writable - - /* - * .dynamic table - */ - elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); - elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); - elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); - elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); - elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); - elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); - elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); - elfwritedynent(s, DT_RELENT, ELF32RELSIZE); - if(rpath) - elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); - elfwritedynent(s, DT_PLTREL, DT_REL); - elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); - elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); - - elfwritedynent(s, DT_DEBUG, 0); - - // Do not write DT_NULL. elfdynhash will finish it. - } -} - -void -shsym(Elf64_Shdr *sh, Sym *s) -{ - vlong addr; - addr = symaddr(s); - if(sh->flags&SHF_ALLOC) - sh->addr = addr; - sh->off = datoff(addr); - sh->size = s->size; -} - -void -phsh(Elf64_Phdr *ph, Elf64_Shdr *sh) -{ - ph->vaddr = sh->addr; - ph->paddr = ph->vaddr; - ph->off = sh->off; - ph->filesz = sh->size; - ph->memsz = sh->size; - ph->align = sh->addralign; -} - -void asmb(void) { int32 v, magic; - int a, dynsym; - uint32 symo, startva, dwarfoff, machlink, resoff; - ElfEhdr *eh; - ElfPhdr *ph, *pph; - ElfShdr *sh; + uint32 symo, dwarfoff, machlink; Section *sect; Sym *sym; - int o; int i; if(debug['v']) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); + if(iself) + asmbelfsetup(); + sect = segtext.sect; cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + /* output read-only data in text segment (rodata, gosymtab, pclntab, ...) */ for(sect = sect->next; sect != nil; sect = sect->next) { cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); datblk(sect->vaddr, sect->len); @@ -712,19 +613,6 @@ asmb(void) machlink = domacholink(); } - if(iself) { - /* index of elf text section; needed by asmelfsym, double-checked below */ - /* !debug['d'] causes extra sections before the .text section */ - elftextsh = 2; - if(!debug['d']) { - elftextsh += 10; - if(elfverneed) - elftextsh += 2; - } - if(HEADTYPE == Hnetbsd) - elftextsh += 1; - } - symsize = 0; spsize = 0; lcsize = 0; @@ -777,6 +665,9 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); + + if(isobj) + elfemitreloc(); } break; case Hplan9x32: @@ -805,8 +696,6 @@ asmb(void) cseek(0L); switch(HEADTYPE) { default: - if(iself) - goto Elfput; case Hgarbunix: /* garbage */ lputb(0x160L<<16); /* magic and sections */ lputb(0L); /* time and date */ @@ -932,273 +821,15 @@ asmb(void) wputl(0x003E); /* reloc table offset */ wputl(0x0000); /* overlay number */ break; - case Hdarwin: asmbmacho(); break; - - Elfput: - eh = getElfEhdr(); - startva = INITTEXT - HEADR; - resoff = ELFRESERVE; - - /* This null SHdr must appear before all others */ - newElfShdr(elfstr[ElfStrEmpty]); - - /* program header info */ - pph = newElfPhdr(); - pph->type = PT_PHDR; - pph->flags = PF_R + PF_X; - pph->off = eh->ehsize; - pph->vaddr = INITTEXT - HEADR + pph->off; - pph->paddr = INITTEXT - HEADR + pph->off; - pph->align = INITRND; - - /* - * PHDR must be in a loaded segment. Adjust the text - * segment boundaries downwards to include it. - */ - o = segtext.vaddr - pph->vaddr; - segtext.vaddr -= o; - segtext.len += o; - o = segtext.fileoff - pph->off; - segtext.fileoff -= o; - segtext.filelen += o; - - if(!debug['d']) { - /* interpreter */ - sh = newElfShdr(elfstr[ElfStrInterp]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - if(interpreter == nil) { - switch(HEADTYPE) { - case Hlinux: - interpreter = linuxdynld; - break; - case Hfreebsd: - interpreter = freebsddynld; - break; - case Hnetbsd: - interpreter = netbsddynld; - break; - case Hopenbsd: - interpreter = openbsddynld; - break; - } - } - resoff -= elfinterp(sh, startva, resoff, interpreter); - - ph = newElfPhdr(); - ph->type = PT_INTERP; - ph->flags = PF_R; - phsh(ph, sh); - } - - if(HEADTYPE == Hnetbsd) { - sh = newElfShdr(elfstr[ElfStrNoteNetbsdIdent]); - sh->type = SHT_NOTE; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - resoff -= elfnetbsdsig(sh, startva, resoff); - - ph = newElfPhdr(); - ph->type = PT_NOTE; - ph->flags = PF_R; - phsh(ph, sh); - } - - // Additions to the reserved area must be above this line. - USED(resoff); - - elfphload(&segtext); - elfphload(&segdata); - - /* Dynamic linking sections */ - if(!debug['d']) { /* -d suppresses dynamic loader format */ - /* S headers for dynamic linking */ - sh = newElfShdr(elfstr[ElfStrGot]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got", 0)); - - sh = newElfShdr(elfstr[ElfStrGotPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".got.plt", 0)); - - dynsym = eh->shnum; - sh = newElfShdr(elfstr[ElfStrDynsym]); - sh->type = SHT_DYNSYM; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32SYMSIZE; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - // sh->info = index of first non-local symbol (number of local symbols) - shsym(sh, lookup(".dynsym", 0)); - - sh = newElfShdr(elfstr[ElfStrDynstr]); - sh->type = SHT_STRTAB; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup(".dynstr", 0)); - - if(elfverneed) { - sh = newElfShdr(elfstr[ElfStrGnuVersion]); - sh->type = SHT_GNU_VERSYM; - sh->flags = SHF_ALLOC; - sh->addralign = 2; - sh->link = dynsym; - sh->entsize = 2; - shsym(sh, lookup(".gnu.version", 0)); - - sh = newElfShdr(elfstr[ElfStrGnuVersionR]); - sh->type = SHT_GNU_VERNEED; - sh->flags = SHF_ALLOC; - sh->addralign = 4; - sh->info = elfverneed; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".gnu.version_r", 0)); - } - - sh = newElfShdr(elfstr[ElfStrRelPlt]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - sh->info = eh->shnum; // .plt - shsym(sh, lookup(".rel.plt", 0)); - - sh = newElfShdr(elfstr[ElfStrPlt]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC+SHF_EXECINSTR; - sh->entsize = 4; - sh->addralign = 4; - shsym(sh, lookup(".plt", 0)); - - sh = newElfShdr(elfstr[ElfStrHash]); - sh->type = SHT_HASH; - sh->flags = SHF_ALLOC; - sh->entsize = 4; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".hash", 0)); - - sh = newElfShdr(elfstr[ElfStrRel]); - sh->type = SHT_REL; - sh->flags = SHF_ALLOC; - sh->entsize = ELF32RELSIZE; - sh->addralign = 4; - sh->link = dynsym; - shsym(sh, lookup(".rel", 0)); - - /* sh and PT_DYNAMIC for .dynamic section */ - sh = newElfShdr(elfstr[ElfStrDynamic]); - sh->type = SHT_DYNAMIC; - sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 8; - sh->addralign = 4; - sh->link = dynsym+1; // dynstr - shsym(sh, lookup(".dynamic", 0)); - ph = newElfPhdr(); - ph->type = PT_DYNAMIC; - ph->flags = PF_R + PF_W; - phsh(ph, sh); - - /* - * Thread-local storage segment (really just size). - */ - if(tlsoffset != 0) { - ph = newElfPhdr(); - ph->type = PT_TLS; - ph->flags = PF_R; - ph->memsz = -tlsoffset; - ph->align = 4; - } - } - - ph = newElfPhdr(); - ph->type = PT_GNU_STACK; - ph->flags = PF_W+PF_R; - ph->align = 4; - - sh = newElfShstrtab(elfstr[ElfStrShstrtab]); - sh->type = SHT_STRTAB; - sh->addralign = 1; - shsym(sh, lookup(".shstrtab", 0)); - - if(elftextsh != eh->shnum) - diag("elftextsh = %d, want %d", elftextsh, eh->shnum); - for(sect=segtext.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - for(sect=segdata.sect; sect!=nil; sect=sect->next) - elfshbits(sect); - - if(!debug['s']) { - sh = newElfShdr(elfstr[ElfStrSymtab]); - sh->type = SHT_SYMTAB; - sh->off = symo; - sh->size = symsize; - sh->addralign = 4; - sh->entsize = 16; - sh->link = eh->shnum; // link to strtab - - sh = newElfShdr(elfstr[ElfStrStrtab]); - sh->type = SHT_STRTAB; - sh->off = symo+symsize; - sh->size = elfstrsize; - sh->addralign = 1; - - dwarfaddelfheaders(); - } - - /* Main header */ - eh->ident[EI_MAG0] = '\177'; - eh->ident[EI_MAG1] = 'E'; - eh->ident[EI_MAG2] = 'L'; - eh->ident[EI_MAG3] = 'F'; - eh->ident[EI_CLASS] = ELFCLASS32; - eh->ident[EI_DATA] = ELFDATA2LSB; - eh->ident[EI_VERSION] = EV_CURRENT; - switch(HEADTYPE) { - case Hfreebsd: - eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; - break; - case Hnetbsd: - eh->ident[EI_OSABI] = ELFOSABI_NETBSD; - break; - case Hopenbsd: - eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; - break; - } - - eh->type = ET_EXEC; - eh->machine = EM_386; - eh->version = EV_CURRENT; - eh->entry = entryvalue(); - - if(pph != nil) { - pph->filesz = eh->phnum * eh->phentsize; - pph->memsz = pph->filesz; - } - - cseek(0); - a = 0; - a += elfwritehdr(); - a += elfwritephdrs(); - a += elfwriteshdrs(); - a += elfwriteinterp(elfstr[ElfStrInterp]); - if(HEADTYPE == Hnetbsd) - a += elfwritenetbsdsig(elfstr[ElfStrNoteNetbsdIdent]); - if(a > ELFRESERVE) - diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); + case Hlinux: + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + asmbelf(symo); break; - case Hwindows: asmbpe(); break; @@ -1231,80 +862,3 @@ rnd(int32 v, int32 r) v -= c; return v; } - -void -genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) -{ - Auto *a; - Sym *s; - int h; - - s = lookup("etext", 0); - if(s->type == STEXT) - put(s, s->name, 'T', s->value, s->size, s->version, 0); - - for(h=0; h<NHASH; h++) { - for(s=hash[h]; s!=S; s=s->hash) { - if(s->hide) - continue; - switch(s->type&~SSUB) { - case SCONST: - case SRODATA: - case SDATA: - case SELFROSECT: - case SMACHO: - case SMACHOGOT: - case STYPE: - case SSTRING: - case SGOSTRING: - case SWINDOWS: - case SNOPTRDATA: - case SSYMTAB: - case SPCLNTAB: - if(!s->reachable) - continue; - put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SBSS: - case SNOPTRBSS: - if(!s->reachable) - continue; - put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); - continue; - - case SFILE: - put(nil, s->name, 'f', s->value, 0, s->version, 0); - continue; - } - } - } - - for(s = textp; s != nil; s = s->next) { - if(s->text == nil) - continue; - - /* filenames first */ - for(a=s->autom; a; a=a->link) - if(a->type == D_FILE) - put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); - else - if(a->type == D_FILE1) - put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); - - put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); - - /* frame, auto and param after */ - put(nil, ".frame", 'm', s->text->to.offset+4, 0, 0, 0); - - for(a=s->autom; a; a=a->link) - if(a->type == D_AUTO) - put(nil, a->asym->name, 'a', -a->aoffset, 0, 0, a->gotype); - else - if(a->type == D_PARAM) - put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); - } - if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %d\n", symsize); - Bflush(&bso); -} diff --git a/src/cmd/8l/doc.go b/src/cmd/8l/doc.go index 12301d4f2..ff06bc376 100644 --- a/src/cmd/8l/doc.go +++ b/src/cmd/8l/doc.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. +// +build ignore + /* 8l is the linker for the 32-bit x86. @@ -10,4 +12,4 @@ The $GOARCH for these tools is 386. The flags are documented in ../ld/doc.go. */ -package documentation +package main diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index b974f464b..f88f058e3 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -40,7 +40,9 @@ enum { thechar = '8', - PtrSize = 4 + PtrSize = 4, + IntSize = 4, + FuncAlign = 16 }; #define P ((Prog*)0) @@ -135,16 +137,22 @@ struct Sym int32 plt; int32 got; int32 align; // if non-zero, required alignment in bytes + int32 elfsym; + int32 locals; // size of stack frame locals area + int32 args; // size of stack frame incoming arguments area Sym* hash; // in hash table Sym* allsym; // in all symbol list Sym* next; // in text or data list Sym* sub; // in sub list Sym* outer; // container of sub Sym* gotype; + Sym* reachparent; + Sym* queue; char* file; char* dynimpname; char* dynimplib; char* dynimpvers; + struct Section* sect; // STEXT Auto* autom; @@ -157,6 +165,7 @@ struct Sym Reloc* r; int32 nr; int32 maxr; + int rel_ro; }; struct Optab { @@ -201,6 +210,8 @@ enum Ycr0, Ycr1, Ycr2, Ycr3, Ycr4, Ycr5, Ycr6, Ycr7, Ydr0, Ydr1, Ydr2, Ydr3, Ydr4, Ydr5, Ydr6, Ydr7, Ytr0, Ytr1, Ytr2, Ytr3, Ytr4, Ytr5, Ytr6, Ytr7, + Ymr, Ymm, + Yxr, Yxm, Ymax, Zxxx = 0, @@ -222,10 +233,14 @@ enum Zloop, Zm_o, Zm_r, + Zm_r_xm, + Zm_r_i_xm, Zaut_r, Zo_m, Zpseudo, Zr_m, + Zr_m_xm, + Zr_m_i_xm, Zrp_, Z_ib, Z_il, @@ -243,6 +258,8 @@ enum Pm = 0x0f, /* 2byte opcode escape */ Pq = 0xff, /* both escape */ Pb = 0xfe, /* byte operands */ + Pf2 = 0xf2, /* xmm escape 1 */ + Pf3 = 0xf3, /* xmm escape 2 */ }; #pragma varargck type "A" int @@ -261,6 +278,7 @@ EXTERN int32 INITRND; EXTERN int32 INITTEXT; EXTERN int32 INITDAT; EXTERN char* INITENTRY; /* entry point */ +EXTERN char* LIBINITENTRY; /* shared library entry point */ EXTERN int32 casepc; EXTERN char* pcstr; EXTERN Auto* curauto; @@ -269,7 +287,7 @@ EXTERN Prog* curp; EXTERN Sym* cursym; EXTERN Sym* datap; EXTERN int32 elfdatsize; -EXTERN char debug[128]; +EXTERN int debug[128]; EXTERN char literal[32]; EXTERN Sym* etextp; EXTERN Prog* firstp; @@ -282,7 +300,6 @@ EXTERN int maxop; EXTERN int nerrors; EXTERN char* noname; EXTERN int32 pc; -EXTERN char* interpreter; EXTERN char* rpath; EXTERN int32 spsize; EXTERN Sym* symlist; diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 31ae02346..0b544fbce 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -254,6 +254,15 @@ char* regstr[] = "TR5", "TR6", "TR7", + + "X0", + "X1", + "X2", + "X3", + "X4", + "X5", + "X6", + "X7", "NONE", /* [D_NONE] */ }; diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index af4bc844f..dcb8390b9 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -77,18 +77,8 @@ Header headers[] = { */ void -usage(void) -{ - fprint(2, "usage: 8l [-options] [-E entry] [-H head] [-I interpreter] [-L dir] [-T text] [-R rnd] [-r path] [-o out] main.8\n"); - exits("usage"); -} - -void main(int argc, char *argv[]) { - int c; - char *name, *val; - Binit(&bso, 1, OWRITE); listinit(); memset(debug, 0, sizeof(debug)); @@ -99,52 +89,44 @@ main(int argc, char *argv[]) INITDAT = -1; INITRND = -1; INITENTRY = 0; + LIBINITENTRY = 0; nuxiinit(); - ARGBEGIN { - default: - c = ARGC(); - if(c == 'l') - usage(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - case 'o': /* output to (next arg) */ - outfile = EARGF(usage()); - break; - case 'E': - INITENTRY = EARGF(usage()); - break; - case 'H': - HEADTYPE = headtype(EARGF(usage())); - break; - case 'I': - interpreter = EARGF(usage()); - break; - case 'L': - Lflag(EARGF(usage())); - break; - case 'T': - INITTEXT = atolwhex(EARGF(usage())); - break; - case 'D': - INITDAT = atolwhex(EARGF(usage())); - break; - case 'R': - INITRND = atolwhex(EARGF(usage())); - break; - case 'r': - rpath = EARGF(usage()); - break; - case 'V': - print("%cl version %s\n", thechar, getgoversion()); - errorexit(); - case 'X': - name = EARGF(usage()); - val = EARGF(usage()); - addstrdata(name, val); - break; - } ARGEND + flagcount("1", "use alternate profiling code", &debug['1']); + flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagstr("E", "sym: entry symbol", &INITENTRY); + flagint32("D", "addr: data address", &INITDAT); + flagfn1("I", "interp: set ELF interp", setinterp); + flagfn1("L", "dir: add dir to library path", Lflag); + flagfn1("H", "head: header type", setheadtype); + flagcount("K", "add stack underflow checks", &debug['K']); + flagcount("O", "print pc-line tables", &debug['O']); + flagcount("Q", "debug byte-register code gen", &debug['Q']); + flagint32("R", "rnd: address rounding", &INITRND); + flagcount("S", "check type signatures", &debug['S']); + flagint32("T", "addr: text address", &INITTEXT); + flagfn0("V", "print version and exit", doversion); + flagcount("W", "disassemble input", &debug['W']); + flagfn2("X", "name value: define string data", addstrdata); + flagcount("Z", "clear stack frame on entry", &debug['Z']); + flagcount("a", "disassemble output", &debug['a']); + flagcount("c", "dump call graph", &debug['c']); + flagcount("d", "disable dynamic executable", &debug['d']); + flagcount("f", "ignore version mismatch", &debug['f']); + flagcount("g", "disable go package data checks", &debug['g']); + flagcount("hostobj", "generate host object file", &isobj); + flagstr("k", "sym: set field tracking symbol", &tracksym); + flagstr("o", "outfile: set output file", &outfile); + flagcount("p", "insert profiling code", &debug['p']); + flagstr("r", "dir1:dir2:...: set ELF dynamic linker search path", &rpath); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "disable symbol table", &debug['s']); + flagcount("n", "dump symbol table", &debug['n']); + flagcount("u", "reject unsafe packages", &debug['u']); + flagcount("v", "print link trace", &debug['v']); + flagcount("w", "disable DWARF generation", &debug['w']); + + flagparse(&argc, &argv, usage); if(argc != 1) usage(); @@ -154,6 +136,15 @@ main(int argc, char *argv[]) if(HEADTYPE == -1) HEADTYPE = headtype(goos); + if(isobj) { + switch(HEADTYPE) { + default: + sysfatal("cannot use -hostobj with -H %s", headstr(HEADTYPE)); + case Hlinux: + break; + } + } + if(outfile == nil) { if(HEADTYPE == Hwindows) outfile = "8.out.exe"; @@ -300,6 +291,7 @@ main(int argc, char *argv[]) if(HEADTYPE == Hwindows) dope(); dostkoff(); + dostkcheck(); if(debug['p']) if(debug['1']) doprof1(); @@ -332,7 +324,7 @@ zsym(char *pn, Biobuf *f, Sym *h[]) { int o; - o = Bgetc(f); + o = BGETC(f); if(o < 0 || o >= NSYM || h[o] == nil) mangle(pn); return h[o]; @@ -346,12 +338,12 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) Sym *s; Auto *u; - t = Bgetc(f); + t = BGETC(f); a->index = D_NONE; a->scale = 0; if(t & T_INDEX) { - a->index = Bgetc(f); - a->scale = Bgetc(f); + a->index = BGETC(f); + a->scale = BGETC(f); } a->type = D_NONE; a->offset = 0; @@ -375,7 +367,7 @@ zaddr(char *pn, Biobuf *f, Adr *a, Sym *h[]) a->type = D_SCONST; } if(t & T_TYPE) - a->type = Bgetc(f); + a->type = BGETC(f); adrgotype = S; if(t & T_GOTYPE) adrgotype = zsym(pn, f, h); @@ -451,10 +443,10 @@ newloop: loop: if(f->state == Bracteof || Boffset(f) >= eof) goto eof; - o = Bgetc(f); + o = BGETC(f); if(o == Beof) goto eof; - o |= Bgetc(f) << 8; + o |= BGETC(f) << 8; if(o <= AXXX || o >= ALAST) { if(o < 0) goto eof; @@ -467,8 +459,8 @@ loop: sig = 0; if(o == ASIGNAME) sig = Bget4(f); - v = Bgetc(f); /* type */ - o = Bgetc(f); /* sym */ + v = BGETC(f); /* type */ + o = BGETC(f); /* sym */ r = 0; if(v == D_STATIC) r = version; @@ -523,8 +515,6 @@ loop: p->as = o; p->line = Bget4(f); p->back = 2; - p->ft = 0; - p->tt = 0; zaddr(pn, f, &p->from, h); fromgotype = adrgotype; zaddr(pn, f, &p->to, h); @@ -605,6 +595,15 @@ loop: pc++; goto loop; + case ALOCALS: + cursym->locals = p->to.offset; + pc++; + goto loop; + + case ATYPE: + pc++; + goto loop; + case ATEXT: s = p->from.sym; if(s->text != nil) { @@ -643,6 +642,7 @@ loop: } s->type = STEXT; s->value = pc; + s->args = p->to.offset2; lastp = p; p->pc = pc++; goto loop; @@ -656,6 +656,13 @@ loop: case AFDIVRF: case AFCOMF: case AFCOMFP: + case AMOVSS: + case AADDSS: + case ASUBSS: + case AMULSS: + case ADIVSS: + case ACOMISS: + case AUCOMISS: if(skip) goto casdef; if(p->from.type == D_FCONST) { @@ -663,7 +670,7 @@ loop: sprint(literal, "$%ux", ieeedtof(&p->from.ieee)); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, ieeedtof(&p->from.ieee)); s->reachable = 0; } @@ -682,6 +689,13 @@ loop: case AFDIVRD: case AFCOMD: case AFCOMDP: + case AMOVSD: + case AADDSD: + case ASUBSD: + case AMULSD: + case ADIVSD: + case ACOMISD: + case AUCOMISD: if(skip) goto casdef; if(p->from.type == D_FCONST) { @@ -690,7 +704,7 @@ loop: p->from.ieee.l, p->from.ieee.h); s = lookup(literal, 0); if(s->type == 0) { - s->type = SDATA; + s->type = SRODATA; adduint32(s, p->from.ieee.l); adduint32(s, p->from.ieee.h); s->reachable = 0; diff --git a/src/cmd/8l/optab.c b/src/cmd/8l/optab.c index 856482290..79d7b39f0 100644 --- a/src/cmd/8l/optab.c +++ b/src/cmd/8l/optab.c @@ -242,6 +242,11 @@ uchar ypopl[] = Ynone, Ym, Zo_m, 2, 0 }; +uchar ybswap[] = +{ + Ynone, Yrl, Z_rp, 1, + 0, +}; uchar yscond[] = { Ynone, Ymb, Zo_m, 2, @@ -249,7 +254,9 @@ uchar yscond[] = }; uchar yjcond[] = { - Ynone, Ybr, Zbr, 1, + Ynone, Ybr, Zbr, 0, + Yi0, Ybr, Zbr, 0, + Yi1, Ybr, Zbr, 1, 0 }; uchar yloop[] = @@ -259,7 +266,8 @@ uchar yloop[] = }; uchar ycall[] = { - Ynone, Yml, Zo_m, 2, + Ynone, Yml, Zo_m, 0, + Yrx, Yrx, Zo_m, 2, Ynone, Ycol, Zcallind, 2, Ynone, Ybr, Zcall, 0, Ynone, Yi32, Zcallcon, 1, @@ -349,6 +357,84 @@ uchar ysvrs[] = Ym, Ynone, Zm_o, 2, 0 }; +uchar yxm[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcvm1[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Yxm, Ymr, Zm_r_xm, 2, + 0 +}; +uchar yxcvm2[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + Ymm, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxmq[] = +{ + Yxm, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxr[] = +{ + Yxr, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxr_ml[] = +{ + Yxr, Yml, Zr_m_xm, 1, + 0 +}; +uchar yxcmp[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcmpi[] = +{ + Yxm, Yxr, Zm_r_i_xm, 2, + 0 +}; +uchar yxmov[] = +{ + Yxm, Yxr, Zm_r_xm, 1, + Yxr, Yxm, Zr_m_xm, 1, + 0 +}; +uchar yxcvfl[] = +{ + Yxm, Yrl, Zm_r_xm, 1, + 0 +}; +uchar yxcvlf[] = +{ + Yml, Yxr, Zm_r_xm, 1, + 0 +}; +uchar yxcvfq[] = +{ + Yxm, Yrl, Zm_r_xm, 2, + 0 +}; +uchar yxcvqf[] = +{ + Yml, Yxr, Zm_r_xm, 2, + 0 +}; +uchar yxrrl[] = +{ + Yxr, Yrl, Zm_r, 1, + 0 +}; +uchar yprefetch[] = +{ + Ym, Ynone, Zm_o, 2, + 0, +}; Optab optab[] = /* as, ytab, andproto, opcode */ @@ -761,5 +847,123 @@ Optab optab[] = { AEMMS, ynone, Pm, 0x77 }, + { APREFETCHT0, yprefetch, Pm, 0x18,(01) }, + { APREFETCHT1, yprefetch, Pm, 0x18,(02) }, + { APREFETCHT2, yprefetch, Pm, 0x18,(03) }, + { APREFETCHNTA, yprefetch, Pm, 0x18,(00) }, + + { ABSWAPL, ybswap, Pm, 0xc8 }, + + { AUNDEF, ynone, Px, 0x0f, 0x0b }, + + { AADDPD, yxm, Pq, 0x58 }, + { AADDPS, yxm, Pm, 0x58 }, + { AADDSD, yxm, Pf2, 0x58 }, + { AADDSS, yxm, Pf3, 0x58 }, + { AANDNPD, yxm, Pq, 0x55 }, + { AANDNPS, yxm, Pm, 0x55 }, + { AANDPD, yxm, Pq, 0x54 }, + { AANDPS, yxm, Pq, 0x54 }, + { ACMPPD, yxcmpi, Px, Pe,0xc2 }, + { ACMPPS, yxcmpi, Pm, 0xc2,0 }, + { ACMPSD, yxcmpi, Px, Pf2,0xc2 }, + { ACMPSS, yxcmpi, Px, Pf3,0xc2 }, + { ACOMISD, yxcmp, Pe, 0x2f }, + { ACOMISS, yxcmp, Pm, 0x2f }, + { ACVTPL2PD, yxcvm2, Px, Pf3,0xe6,Pe,0x2a }, + { ACVTPL2PS, yxcvm2, Pm, 0x5b,0,0x2a,0, }, + { ACVTPD2PL, yxcvm1, Px, Pf2,0xe6,Pe,0x2d }, + { ACVTPD2PS, yxm, Pe, 0x5a }, + { ACVTPS2PL, yxcvm1, Px, Pe,0x5b,Pm,0x2d }, + { ACVTPS2PD, yxm, Pm, 0x5a }, + { ACVTSD2SL, yxcvfl, Pf2, 0x2d }, + { ACVTSD2SS, yxm, Pf2, 0x5a }, + { ACVTSL2SD, yxcvlf, Pf2, 0x2a }, + { ACVTSL2SS, yxcvlf, Pf3, 0x2a }, + { ACVTSS2SD, yxm, Pf3, 0x5a }, + { ACVTSS2SL, yxcvfl, Pf3, 0x2d }, + { ACVTTPD2PL, yxcvm1, Px, Pe,0xe6,Pe,0x2c }, + { ACVTTPS2PL, yxcvm1, Px, Pf3,0x5b,Pm,0x2c }, + { ACVTTSD2SL, yxcvfl, Pf2, 0x2c }, + { ACVTTSS2SL, yxcvfl, Pf3, 0x2c }, + { ADIVPD, yxm, Pe, 0x5e }, + { ADIVPS, yxm, Pm, 0x5e }, + { ADIVSD, yxm, Pf2, 0x5e }, + { ADIVSS, yxm, Pf3, 0x5e }, + { AMASKMOVOU, yxr, Pe, 0xf7 }, + { AMAXPD, yxm, Pe, 0x5f }, + { AMAXPS, yxm, Pm, 0x5f }, + { AMAXSD, yxm, Pf2, 0x5f }, + { AMAXSS, yxm, Pf3, 0x5f }, + { AMINPD, yxm, Pe, 0x5d }, + { AMINPS, yxm, Pm, 0x5d }, + { AMINSD, yxm, Pf2, 0x5d }, + { AMINSS, yxm, Pf3, 0x5d }, + { AMOVAPD, yxmov, Pe, 0x28,0x29 }, + { AMOVAPS, yxmov, Pm, 0x28,0x29 }, + { AMOVO, yxmov, Pe, 0x6f,0x7f }, + { AMOVOU, yxmov, Pf3, 0x6f,0x7f }, + { AMOVHLPS, yxr, Pm, 0x12 }, + { AMOVHPD, yxmov, Pe, 0x16,0x17 }, + { AMOVHPS, yxmov, Pm, 0x16,0x17 }, + { AMOVLHPS, yxr, Pm, 0x16 }, + { AMOVLPD, yxmov, Pe, 0x12,0x13 }, + { AMOVLPS, yxmov, Pm, 0x12,0x13 }, + { AMOVMSKPD, yxrrl, Pq, 0x50 }, + { AMOVMSKPS, yxrrl, Pm, 0x50 }, + { AMOVNTO, yxr_ml, Pe, 0xe7 }, + { AMOVNTPD, yxr_ml, Pe, 0x2b }, + { AMOVNTPS, yxr_ml, Pm, 0x2b }, + { AMOVSD, yxmov, Pf2, 0x10,0x11 }, + { AMOVSS, yxmov, Pf3, 0x10,0x11 }, + { AMOVUPD, yxmov, Pe, 0x10,0x11 }, + { AMOVUPS, yxmov, Pm, 0x10,0x11 }, + { AMULPD, yxm, Pe, 0x59 }, + { AMULPS, yxm, Ym, 0x59 }, + { AMULSD, yxm, Pf2, 0x59 }, + { AMULSS, yxm, Pf3, 0x59 }, + { AORPD, yxm, Pq, 0x56 }, + { AORPS, yxm, Pm, 0x56 }, + { APADDQ, yxm, Pe, 0xd4 }, + { APMAXSW, yxm, Pe, 0xee }, + { APMAXUB, yxm, Pe, 0xde }, + { APMINSW, yxm, Pe, 0xea }, + { APMINUB, yxm, Pe, 0xda }, + { APSADBW, yxm, Pq, 0xf6 }, + { APSUBB, yxm, Pe, 0xf8 }, + { APSUBL, yxm, Pe, 0xfa }, + { APSUBQ, yxm, Pe, 0xfb }, + { APSUBSB, yxm, Pe, 0xe8 }, + { APSUBSW, yxm, Pe, 0xe9 }, + { APSUBUSB, yxm, Pe, 0xd8 }, + { APSUBUSW, yxm, Pe, 0xd9 }, + { APSUBW, yxm, Pe, 0xf9 }, + { APUNPCKHQDQ, yxm, Pe, 0x6d }, + { APUNPCKLQDQ, yxm, Pe, 0x6c }, + { ARCPPS, yxm, Pm, 0x53 }, + { ARCPSS, yxm, Pf3, 0x53 }, + { ARSQRTPS, yxm, Pm, 0x52 }, + { ARSQRTSS, yxm, Pf3, 0x52 }, + { ASQRTPD, yxm, Pe, 0x51 }, + { ASQRTPS, yxm, Pm, 0x51 }, + { ASQRTSD, yxm, Pf2, 0x51 }, + { ASQRTSS, yxm, Pf3, 0x51 }, + { ASUBPD, yxm, Pe, 0x5c }, + { ASUBPS, yxm, Pm, 0x5c }, + { ASUBSD, yxm, Pf2, 0x5c }, + { ASUBSS, yxm, Pf3, 0x5c }, + { AUCOMISD, yxcmp, Pe, 0x2e }, + { AUCOMISS, yxcmp, Pm, 0x2e }, + { AUNPCKHPD, yxm, Pe, 0x15 }, + { AUNPCKHPS, yxm, Pm, 0x15 }, + { AUNPCKLPD, yxm, Pe, 0x14 }, + { AUNPCKLPS, yxm, Pm, 0x14 }, + { AXORPD, yxm, Pe, 0x57 }, + { AXORPS, yxm, Pm, 0x57 }, + + { AUSEFIELD, ynop, Px, 0,0 }, + { ALOCALS }, + { ATYPE }, + 0 }; diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 9034fdf3a..14dd3e0dc 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -75,6 +75,7 @@ nofollow(int a) case ARET: case AIRETL: case AIRETW: + case AUNDEF: return 1; } return 0; @@ -184,20 +185,34 @@ loop: * recurse to follow one path. * continue loop on the other. */ - q = brchain(p->link); - if(q != P && q->mark) - if(a != ALOOP) { - p->as = relinv(a); - p->link = p->pcond; + if((q = brchain(p->pcond)) != P) p->pcond = q; + if((q = brchain(p->link)) != P) + p->link = q; + if(p->from.type == D_CONST) { + if(p->from.offset == 1) { + /* + * expect conditional jump to be taken. + * rewrite so that's the fall-through case. + */ + p->as = relinv(a); + q = p->link; + p->link = p->pcond; + p->pcond = q; + } + } else { + q = p->link; + if(q->mark) + if(a != ALOOP) { + p->as = relinv(a); + p->link = p->pcond; + p->pcond = q; + } } xfol(p->link, last); - q = brchain(p->pcond); - if(q->mark) { - p->pcond = q; + if(p->pcond->mark) return; - } - p = q; + p = p->pcond; goto loop; } p = p->link; @@ -315,7 +330,7 @@ patch(void) } else if(s) { if(debug['c']) Bprint(&bso, "%s calls %s\n", TNAME, s->name); - if((s->type&~SSUB) != STEXT) { + if((s->type&SMASK) != STEXT) { /* diag prints TNAME first */ diag("undefined: %s", s->name); s->type = STEXT; @@ -524,9 +539,9 @@ dostkoff(void) q = p; } - p = appendp(p); // save frame size in DX + p = appendp(p); // save frame size in DI p->as = AMOVL; - p->to.type = D_DX; + p->to.type = D_DI; p->from.type = D_CONST; // If we ask for more stack, we'll get a minimum of StackMin bytes. @@ -565,9 +580,47 @@ dostkoff(void) p->spadj = autoffset; if(q != P) q->pcond = p; + } else { + // zero-byte stack adjustment. + // Insert a fake non-zero adjustment so that stkcheck can + // recognize the end of the stack-splitting prolog. + p = appendp(p); + p->as = ANOP; + p->spadj = -PtrSize; + p = appendp(p); + p->as = ANOP; + p->spadj = PtrSize; } deltasp = autoffset; + if(debug['Z'] && autoffset && !(cursym->text->from.scale&NOSPLIT)) { + // 8l -Z means zero the stack frame on entry. + // This slows down function calls but can help avoid + // false positives in garbage collection. + p = appendp(p); + p->as = AMOVL; + p->from.type = D_SP; + p->to.type = D_DI; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = autoffset/4; + p->to.type = D_CX; + + p = appendp(p); + p->as = AMOVL; + p->from.type = D_CONST; + p->from.offset = 0; + p->to.type = D_AX; + + p = appendp(p); + p->as = AREP; + + p = appendp(p); + p->as = ASTOSL; + } + for(; p != P; p = p->link) { a = p->from.type; if(a == D_AUTO) diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 81c1d37eb..b828d8645 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -194,7 +194,7 @@ instinit(void) for(i=1; optab[i].as; i++) if(i != optab[i].as) { - diag("phase error in optab: %d", i); + diag("phase error in optab: at %A found %A", i, optab[i].as); errorexit(); } maxop = i; @@ -238,6 +238,16 @@ instinit(void) ycover[Yrl*Ymax + Yml] = 1; ycover[Ym*Ymax + Yml] = 1; + ycover[Yax*Ymax + Ymm] = 1; + ycover[Ycx*Ymax + Ymm] = 1; + ycover[Yrx*Ymax + Ymm] = 1; + ycover[Yrl*Ymax + Ymm] = 1; + ycover[Ym*Ymax + Ymm] = 1; + ycover[Ymr*Ymax + Ymm] = 1; + + ycover[Ym*Ymax + Yxm] = 1; + ycover[Yxr*Ymax + Yxm] = 1; + for(i=0; i<D_NONE; i++) { reg[i] = -1; if(i >= D_AL && i <= D_BH) @@ -246,6 +256,8 @@ instinit(void) reg[i] = (i-D_AX) & 7; if(i >= D_F0 && i <= D_F0+7) reg[i] = (i-D_F0) & 7; + if(i >= D_X0 && i <= D_X0+7) + reg[i] = (i-D_X0) & 7; } } @@ -333,6 +345,16 @@ oclass(Adr *a) case D_F0+7: return Yrf; + case D_X0+0: + case D_X0+1: + case D_X0+2: + case D_X0+3: + case D_X0+4: + case D_X0+5: + case D_X0+6: + case D_X0+7: + return Yxr; + case D_NONE: return Ynone; @@ -585,7 +607,7 @@ asmand(Adr *a, int r) asmidx(a->scale, a->index, t); goto putrelv; } - if(t >= D_AL && t <= D_F0+7) { + if(t >= D_AL && t <= D_F7 || t >= D_X0 && t <= D_X7) { if(v) goto bad; *andptr++ = (3 << 6) | (reg[t] << 0) | (r << 3); @@ -772,19 +794,71 @@ uchar ymovtab[] = 0 }; +// byteswapreg returns a byte-addressable register (AX, BX, CX, DX) +// which is not referenced in a->type. +// If a is empty, it returns BX to account for MULB-like instructions +// that might use DX and AX. int -isax(Adr *a) +byteswapreg(Adr *a) { + int cana, canb, canc, cand; + + cana = canb = canc = cand = 1; switch(a->type) { + case D_NONE: + cana = cand = 0; + break; case D_AX: case D_AL: case D_AH: case D_INDIR+D_AX: - return 1; + cana = 0; + break; + case D_BX: + case D_BL: + case D_BH: + case D_INDIR+D_BX: + canb = 0; + break; + case D_CX: + case D_CL: + case D_CH: + case D_INDIR+D_CX: + canc = 0; + break; + case D_DX: + case D_DL: + case D_DH: + case D_INDIR+D_DX: + cand = 0; + break; + } + switch(a->index) { + case D_AX: + cana = 0; + break; + case D_BX: + canb = 0; + break; + case D_CX: + canc = 0; + break; + case D_DX: + cand = 0; + break; } - if(a->index == D_AX) - return 1; + if(cana) + return D_AX; + if(canb) + return D_BX; + if(canc) + return D_CX; + if(cand) + return D_DX; + + diag("impossible byte register"); + errorexit(); return 0; } @@ -827,13 +901,37 @@ subreg(Prog *p, int from, int to) print("%P\n", p); } +static int +mediaop(Optab *o, int op, int osize, int z) +{ + switch(op){ + case Pm: + case Pe: + case Pf2: + case Pf3: + if(osize != 1){ + if(op != Pm) + *andptr++ = op; + *andptr++ = Pm; + op = o->op[++z]; + break; + } + default: + if(andptr == and || andptr[-1] != Pm) + *andptr++ = Pm; + break; + } + *andptr++ = op; + return z; +} + void doasm(Prog *p) { Optab *o; Prog *q, pp; uchar *t; - int z, op, ft, tt; + int z, op, ft, tt, breg; int32 v, pre; Reloc rel, *r; Adr *a; @@ -873,6 +971,12 @@ found: *andptr++ = Pm; break; + case Pf2: /* xmm opcode escape */ + case Pf3: + *andptr++ = o->prefix; + *andptr++ = Pm; + break; + case Pm: /* opcode escape */ *andptr++ = Pm; break; @@ -904,6 +1008,17 @@ found: asmand(&p->from, reg[p->to.type]); break; + case Zm_r_xm: + mediaop(o, op, t[3], z); + asmand(&p->from, reg[p->to.type]); + break; + + case Zm_r_i_xm: + mediaop(o, op, t[3], z); + asmand(&p->from, reg[p->to.type]); + *andptr++ = p->to.offset; + break; + case Zaut_r: *andptr++ = 0x8d; /* leal */ if(p->from.type != D_ADDR) @@ -927,6 +1042,17 @@ found: asmand(&p->to, reg[p->from.type]); break; + case Zr_m_xm: + mediaop(o, op, t[3], z); + asmand(&p->to, reg[p->from.type]); + break; + + case Zr_m_i_xm: + mediaop(o, op, t[3], z); + asmand(&p->to, reg[p->from.type]); + *andptr++ = p->from.offset; + break; + case Zo_m: *andptr++ = op; asmand(&p->to, o->op[z+1]); @@ -1198,13 +1324,13 @@ bad: pp = *p; z = p->from.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->to)) { + if((breg = byteswapreg(&p->to)) != D_AX) { *andptr++ = 0x87; /* xchg lhs,bx */ - asmand(&p->from, reg[D_BX]); - subreg(&pp, z, D_BX); + asmand(&p->from, reg[breg]); + subreg(&pp, z, breg); doasm(&pp); *andptr++ = 0x87; /* xchg lhs,bx */ - asmand(&p->from, reg[D_BX]); + asmand(&p->from, reg[breg]); } else { *andptr++ = 0x90 + reg[z]; /* xchg lsh,ax */ subreg(&pp, z, D_AX); @@ -1215,13 +1341,13 @@ bad: } z = p->to.type; if(z >= D_BP && z <= D_DI) { - if(isax(&p->from)) { + if((breg = byteswapreg(&p->from)) != D_AX) { *andptr++ = 0x87; /* xchg rhs,bx */ - asmand(&p->to, reg[D_BX]); - subreg(&pp, z, D_BX); + asmand(&p->to, reg[breg]); + subreg(&pp, z, breg); doasm(&pp); *andptr++ = 0x87; /* xchg rhs,bx */ - asmand(&p->to, reg[D_BX]); + asmand(&p->to, reg[breg]); } else { *andptr++ = 0x90 + reg[z]; /* xchg rsh,ax */ subreg(&pp, z, D_AX); diff --git a/src/cmd/api/clone.go b/src/cmd/api/clone.go new file mode 100644 index 000000000..180215f4b --- /dev/null +++ b/src/cmd/api/clone.go @@ -0,0 +1,251 @@ +// 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 main + +import ( + "fmt" + "go/ast" + "log" + "reflect" +) + +const debugClone = false + +// TODO(bradfitz): delete this function (and whole file) once +// http://golang.org/issue/4380 is fixed. +func clone(i interface{}) (cloned interface{}) { + if debugClone { + defer func() { + if !reflect.DeepEqual(i, cloned) { + log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned) + } + }() + } + switch v := i.(type) { + case nil: + return nil + case *ast.File: + o := &ast.File{ + Doc: v.Doc, // shallow + Package: v.Package, + Comments: v.Comments, // shallow + Name: v.Name, + Scope: v.Scope, + } + for _, x := range v.Decls { + o.Decls = append(o.Decls, clone(x).(ast.Decl)) + } + for _, x := range v.Imports { + o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec)) + } + for _, x := range v.Unresolved { + o.Unresolved = append(o.Unresolved, x) + } + return o + case *ast.GenDecl: + o := new(ast.GenDecl) + *o = *v + o.Specs = nil + for _, x := range v.Specs { + o.Specs = append(o.Specs, clone(x).(ast.Spec)) + } + return o + case *ast.TypeSpec: + o := new(ast.TypeSpec) + *o = *v + o.Type = cloneExpr(v.Type) + return o + case *ast.InterfaceType: + o := new(ast.InterfaceType) + *o = *v + o.Methods = clone(v.Methods).(*ast.FieldList) + return o + case *ast.FieldList: + if v == nil { + return v + } + o := new(ast.FieldList) + *o = *v + o.List = nil + for _, x := range v.List { + o.List = append(o.List, clone(x).(*ast.Field)) + } + return o + case *ast.Field: + o := &ast.Field{ + Doc: v.Doc, // shallow + Type: cloneExpr(v.Type), + Tag: clone(v.Tag).(*ast.BasicLit), + Comment: v.Comment, // shallow + } + for _, x := range v.Names { + o.Names = append(o.Names, clone(x).(*ast.Ident)) + } + return o + case *ast.FuncType: + if v == nil { + return v + } + return &ast.FuncType{ + Func: v.Func, + Params: clone(v.Params).(*ast.FieldList), + Results: clone(v.Results).(*ast.FieldList), + } + case *ast.FuncDecl: + if v == nil { + return v + } + return &ast.FuncDecl{ + Recv: clone(v.Recv).(*ast.FieldList), + Name: v.Name, + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.ValueSpec: + if v == nil { + return v + } + o := &ast.ValueSpec{ + Type: cloneExpr(v.Type), + } + for _, x := range v.Names { + o.Names = append(o.Names, x) + } + for _, x := range v.Values { + o.Values = append(o.Values, cloneExpr(x)) + } + return o + case *ast.CallExpr: + if v == nil { + return v + } + o := &ast.CallExpr{} + *o = *v + o.Args = cloneExprs(v.Args) + o.Fun = cloneExpr(v.Fun) + return o + case *ast.SelectorExpr: + if v == nil { + return nil + } + return &ast.SelectorExpr{ + X: cloneExpr(v.X), + Sel: v.Sel, + } + case *ast.ArrayType: + return &ast.ArrayType{ + Lbrack: v.Lbrack, + Len: cloneExpr(v.Len), + Elt: cloneExpr(v.Elt), + } + case *ast.StructType: + return &ast.StructType{ + Struct: v.Struct, + Fields: clone(v.Fields).(*ast.FieldList), + Incomplete: v.Incomplete, + } + case *ast.StarExpr: + return &ast.StarExpr{ + Star: v.Star, + X: cloneExpr(v.X), + } + case *ast.CompositeLit: + return &ast.CompositeLit{ + Type: cloneExpr(v.Type), + Lbrace: v.Lbrace, + Elts: cloneExprs(v.Elts), + Rbrace: v.Rbrace, + } + case *ast.UnaryExpr: + return &ast.UnaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + } + case *ast.BinaryExpr: + return &ast.BinaryExpr{ + OpPos: v.OpPos, + Op: v.Op, + X: cloneExpr(v.X), + Y: cloneExpr(v.Y), + } + case *ast.Ellipsis: + return &ast.Ellipsis{ + Ellipsis: v.Ellipsis, + Elt: cloneExpr(v.Elt), + } + case *ast.KeyValueExpr: + return &ast.KeyValueExpr{ + Key: cloneExpr(v.Key), + Colon: v.Colon, + Value: cloneExpr(v.Value), + } + case *ast.FuncLit: + return &ast.FuncLit{ + Type: clone(v.Type).(*ast.FuncType), + Body: v.Body, // shallow + } + case *ast.MapType: + return &ast.MapType{ + Map: v.Map, + Key: cloneExpr(v.Key), + Value: cloneExpr(v.Value), + } + case *ast.ParenExpr: + return &ast.ParenExpr{ + Lparen: v.Lparen, + X: cloneExpr(v.X), + Rparen: v.Rparen, + } + case *ast.Ident, *ast.BasicLit: + return v + case *ast.ImportSpec: + return &ast.ImportSpec{ + Doc: v.Doc, // shallow + Name: v.Name, + Path: clone(v.Path).(*ast.BasicLit), + Comment: v.Comment, // shallow + EndPos: v.EndPos, + } + case *ast.ChanType: + return &ast.ChanType{ + Begin: v.Begin, + Arrow: v.Arrow, + Dir: v.Dir, + Value: cloneExpr(v.Value), + } + case *ast.TypeAssertExpr: + return &ast.TypeAssertExpr{ + X: cloneExpr(v.X), + Type: cloneExpr(v.Type), + } + case *ast.IndexExpr: + return &ast.IndexExpr{ + X: cloneExpr(v.X), + Index: cloneExpr(v.Index), + Lbrack: v.Lbrack, + Rbrack: v.Rbrack, + } + } + panic(fmt.Sprintf("Uncloneable type %T", i)) +} + +func cloneExpr(x ast.Expr) ast.Expr { + if x == nil { + return nil + } + return clone(x).(ast.Expr) +} + +func cloneExprs(x []ast.Expr) []ast.Expr { + if x == nil { + return nil + } + o := make([]ast.Expr, len(x)) + for i, x := range x { + o[i] = cloneExpr(x) + } + return o +} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index ad1c6bb8c..0d76b0cdb 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -22,12 +22,14 @@ import ( "go/parser" "go/printer" "go/token" + "io" "io/ioutil" "log" "os" "os/exec" "path" "path/filepath" + "regexp" "runtime" "sort" "strconv" @@ -38,11 +40,12 @@ import ( var ( // TODO(bradfitz): once Go 1.1 comes out, allow the -c flag to take a comma-separated // list of files, rather than just one. - checkFile = flag.String("c", "", "optional filename to check API against") - allowNew = flag.Bool("allow_new", true, "allow API additions") - 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 <goos>-<goarch>[-cgo] to override default contexts.") + checkFile = flag.String("c", "", "optional filename 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 <goos>-<goarch>[-cgo] to override default contexts.") ) // contexts are the default contexts which are scanned, unless @@ -100,7 +103,7 @@ func setContexts() { func main() { flag.Parse() - if !strings.Contains(runtime.Version(), "weekly") { + 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 = "" @@ -166,7 +169,6 @@ func main() { features = append(features, f2) } } - sort.Strings(features) fail := false defer func() { @@ -179,24 +181,45 @@ func main() { defer bw.Flush() if *checkFile == "" { + sort.Strings(features) for _, f := range features { fmt.Fprintf(bw, "%s\n", f) } return } - var required []string - for _, filename := range []string{*checkFile} { - required = append(required, fileFeatures(filename)...) + required := fileFeatures(*checkFile) + optional := fileFeatures(*nextFile) + exception := fileFeatures(*exceptFile) + fail = !compareAPI(bw, features, required, optional, exception) +} + +func set(items []string) map[string]bool { + s := make(map[string]bool) + for _, v := range items { + s[v] = true } - sort.Strings(required) + return s +} - var optional = make(map[string]bool) // feature => true - if *nextFile != "" { - for _, feature := range fileFeatures(*nextFile) { - optional[feature] = true - } +var spaceParensRx = regexp.MustCompile(` \(\S+?\)`) + +func featureWithoutContext(f string) string { + if !strings.Contains(f, "(") { + return f } + return spaceParensRx.ReplaceAllString(f, "") +} + +func compareAPI(w io.Writer, features, required, optional, exception []string) (ok bool) { + ok = true + + optionalSet := set(optional) + exceptionSet := set(exception) + featureSet := set(features) + + sort.Strings(features) + sort.Strings(required) take := func(sl *[]string) string { s := (*sl)[0] @@ -206,20 +229,27 @@ func main() { for len(required) > 0 || len(features) > 0 { switch { - case len(features) == 0 || required[0] < features[0]: - fmt.Fprintf(bw, "-%s\n", take(&required)) - fail = true // broke compatibility - case len(required) == 0 || required[0] > features[0]: + case len(features) == 0 || (len(required) > 0 && required[0] < features[0]): + feature := take(&required) + if exceptionSet[feature] { + fmt.Fprintf(w, "~%s\n", feature) + } else if featureSet[featureWithoutContext(feature)] { + // okay. + } else { + fmt.Fprintf(w, "-%s\n", feature) + ok = false // broke compatibility + } + case len(required) == 0 || (len(features) > 0 && required[0] > features[0]): newFeature := take(&features) - if optional[newFeature] { + if optionalSet[newFeature] { // Known added feature to the upcoming release. // Delete it from the map so we can detect any upcoming features // which were never seen. (so we can clean up the nextFile) - delete(optional, newFeature) + delete(optionalSet, newFeature) } else { - fmt.Fprintf(bw, "+%s\n", newFeature) + fmt.Fprintf(w, "+%s\n", newFeature) if !*allowNew { - fail = true // we're in lock-down mode for next release + ok = false // we're in lock-down mode for next release } } default: @@ -228,17 +258,22 @@ func main() { } } + // In next file, but not in API. var missing []string - for feature := range optional { + for feature := range optionalSet { missing = append(missing, feature) } sort.Strings(missing) for _, feature := range missing { - fmt.Fprintf(bw, "(in next file, but not in API) -%s\n", feature) + fmt.Fprintf(w, "±%s\n", feature) } + return } func fileFeatures(filename string) []string { + if filename == "" { + return nil + } bs, err := ioutil.ReadFile(filename) if err != nil { log.Fatalf("Error reading file %s: %v", filename, err) @@ -256,10 +291,11 @@ type pkgSymbol struct { symbol string // "RoundTripper" } +var fset = token.NewFileSet() + type Walker struct { context *build.Context root string - fset *token.FileSet scope []string features map[string]bool // set lastConstType string @@ -276,7 +312,6 @@ type Walker struct { func NewWalker() *Walker { return &Walker{ - fset: token.NewFileSet(), features: make(map[string]bool), packageState: make(map[string]loadState), interfaces: make(map[pkgSymbol]*ast.InterfaceType), @@ -297,20 +332,6 @@ const ( loaded ) -// hardCodedConstantType is a hack until the type checker is sufficient for our needs. -// Rather than litter the code with unnecessary type annotations, we'll hard-code -// the cases we can't handle yet. -func (w *Walker) hardCodedConstantType(name string) (typ string, ok bool) { - switch w.scope[0] { - case "pkg syscall": - switch name { - case "darwinAMD64": - return "bool", true - } - } - return "", false -} - func (w *Walker) Features() (fs []string) { for f := range w.features { fs = append(fs, f) @@ -333,6 +354,21 @@ func fileDeps(f *ast.File) (pkgs []string) { return } +var parsedFileCache = make(map[string]*ast.File) + +func parseFile(filename string) (*ast.File, error) { + f, ok := parsedFileCache[filename] + if !ok { + var err error + f, err = parser.ParseFile(fset, filename, nil, 0) + if err != nil { + return nil, err + } + parsedFileCache[filename] = f + } + return clone(f).(*ast.File), nil +} + // WalkPackage walks all files in package `name'. // WalkPackage does nothing if the package has already been loaded. func (w *Walker) WalkPackage(name string) { @@ -366,7 +402,7 @@ func (w *Walker) WalkPackage(name string) { files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) for _, file := range files { - f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) + f, err := parseFile(filepath.Join(dir, file)) if err != nil { log.Fatalf("error parsing package %s, file %s: %v", name, file, err) } @@ -501,7 +537,7 @@ func (w *Walker) walkFile(file *ast.File) { // Ignore. Handled in subsequent pass, by go/doc. default: log.Printf("unhandled %T, %#v\n", di, di) - printer.Fprint(os.Stderr, w.fset, di) + printer.Fprint(os.Stderr, fset, di) os.Stderr.Write([]byte("\n")) } } @@ -562,6 +598,10 @@ func (w *Walker) constValueType(vi interface{}) (string, error) { } return constDepPrefix + v.Name, nil case *ast.BinaryExpr: + switch v.Op { + case token.EQL, token.LSS, token.GTR, token.NOT, token.NEQ, token.LEQ, token.GEQ: + return "bool", nil + } left, err := w.constValueType(v.X) if err != nil { return "", err @@ -734,17 +774,11 @@ func (w *Walker) walkConst(vs *ast.ValueSpec) { var err error litType, err = w.constValueType(vs.Values[0]) if err != nil { - if t, ok := w.hardCodedConstantType(ident.Name); ok { - litType = t - err = nil - } else { - log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) - } + log.Fatalf("unknown kind in const %q (%T): %v", ident.Name, vs.Values[0], err) } } } - if strings.HasPrefix(litType, constDepPrefix) { - dep := litType[len(constDepPrefix):] + if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType { w.constDep[ident.Name] = dep continue } @@ -816,7 +850,7 @@ func (w *Walker) nodeString(node interface{}) string { return "" } var b bytes.Buffer - printer.Fprint(&b, w.fset, node) + printer.Fprint(&b, fset, node) return b.String() } @@ -825,7 +859,7 @@ func (w *Walker) nodeDebug(node interface{}) string { return "" } var b bytes.Buffer - ast.Fprint(&b, w.fset, node, nil) + ast.Fprint(&b, fset, node, nil) return b.String() } @@ -844,7 +878,7 @@ func (w *Walker) walkTypeSpec(ts *ast.TypeSpec) { case *ast.InterfaceType: w.walkInterfaceType(name, t) default: - w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(ts.Type))) + w.emitFeature(fmt.Sprintf("type %s %s", name, w.nodeString(w.namelessType(ts.Type)))) } } @@ -892,15 +926,18 @@ type method struct { sig string // "([]byte) (int, error)", from funcSigString } -// interfaceMethods returns the expanded list of methods for an interface. +// interfaceMethods returns the expanded list of exported methods for an interface. +// The boolean complete reports whether the list contains all methods (that is, the +// interface has no unexported methods). // pkg is the complete package name ("net/http") // iname is the interface name. -func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { +func (w *Walker) interfaceMethods(pkg, iname string) (methods []method, complete bool) { t, ok := w.interfaces[pkgSymbol{pkg, iname}] if !ok { log.Fatalf("failed to find interface %s.%s", pkg, iname) } + complete = true for _, f := range t.Methods.List { typ := f.Type switch tv := typ.(type) { @@ -912,6 +949,8 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { name: mname.Name, sig: w.funcSigString(ft), }) + } else { + complete = false } } case *ast.Ident: @@ -927,7 +966,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { log.Fatalf("unexported embedded interface %q in exported interface %s.%s; confused", embedded, pkg, iname) } - methods = append(methods, w.interfaceMethods(pkg, embedded)...) + m, c := w.interfaceMethods(pkg, embedded) + methods = append(methods, m...) + complete = complete && c case *ast.SelectorExpr: lhs := w.nodeString(tv.X) rhs := w.nodeString(tv.Sel) @@ -935,7 +976,9 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { if !ok { log.Fatalf("can't resolve selector %q in interface %s.%s", lhs, pkg, iname) } - methods = append(methods, w.interfaceMethods(fpkg, rhs)...) + m, c := w.interfaceMethods(fpkg, rhs) + methods = append(methods, m...) + complete = complete && c default: log.Fatalf("unknown type %T in interface field", typ) } @@ -945,14 +988,28 @@ func (w *Walker) interfaceMethods(pkg, iname string) (methods []method) { func (w *Walker) walkInterfaceType(name string, t *ast.InterfaceType) { methNames := []string{} - pop := w.pushScope("type " + name + " interface") - for _, m := range w.interfaceMethods(w.curPackageName, name) { + methods, complete := w.interfaceMethods(w.curPackageName, name) + for _, m := range methods { methNames = append(methNames, m.name) w.emitFeature(fmt.Sprintf("%s%s", m.name, m.sig)) } + if !complete { + // The method set has unexported methods, so all the + // implementations are provided by the same package, + // so the method set can be extended. Instead of recording + // the full set of names (below), record only that there were + // unexported methods. (If the interface shrinks, we will notice + // because a method signature emitted during the last loop, + // will disappear.) + w.emitFeature("unexported methods") + } pop() + if !complete { + return + } + sort.Strings(methNames) if len(methNames) == 0 { w.emitFeature(fmt.Sprintf("type %s interface {}", name)) @@ -994,18 +1051,38 @@ func (w *Walker) walkFuncDecl(f *ast.FuncDecl) { func (w *Walker) funcSigString(ft *ast.FuncType) string { var b bytes.Buffer + writeField := func(b *bytes.Buffer, f *ast.Field) { + if n := len(f.Names); n > 1 { + for i := 0; i < n; i++ { + if i > 0 { + b.WriteString(", ") + } + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } else { + b.WriteString(w.nodeString(w.namelessType(f.Type))) + } + } b.WriteByte('(') if ft.Params != nil { for i, f := range ft.Params.List { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } } b.WriteByte(')') if ft.Results != nil { - if nr := len(ft.Results.List); nr > 0 { + nr := 0 + for _, f := range ft.Results.List { + if n := len(f.Names); n > 1 { + nr += n + } else { + nr++ + } + } + if nr > 0 { b.WriteByte(' ') if nr > 1 { b.WriteByte('(') @@ -1014,7 +1091,7 @@ func (w *Walker) funcSigString(ft *ast.FuncType) string { if i > 0 { b.WriteString(", ") } - b.WriteString(w.nodeString(w.namelessType(f.Type))) + writeField(&b, f) } if nr > 1 { b.WriteByte(')') @@ -1042,7 +1119,13 @@ func (w *Walker) namelessFieldList(fl *ast.FieldList) *ast.FieldList { fl2 := &ast.FieldList{} if fl != nil { for _, f := range fl.List { - fl2.List = append(fl2.List, w.namelessField(f)) + repeats := 1 + if len(f.Names) > 1 { + repeats = len(f.Names) + } + for i := 0; i < repeats; i++ { + fl2.List = append(fl2.List, w.namelessField(f)) + } } } return fl2 @@ -1056,10 +1139,21 @@ func (w *Walker) namelessField(f *ast.Field) *ast.Field { } } +var ( + byteRx = regexp.MustCompile(`\bbyte\b`) + runeRx = regexp.MustCompile(`\brune\b`) +) + func (w *Walker) emitFeature(feature string) { if !w.wantedPkg[w.curPackageName] { return } + if strings.Contains(feature, "byte") { + feature = byteRx.ReplaceAllString(feature, "uint8") + } + if strings.Contains(feature, "rune") { + feature = runeRx.ReplaceAllString(feature, "int32") + } f := strings.Join(w.scope, ", ") + ", " + feature if _, dup := w.features[f]; dup { panic("duplicate feature inserted: " + f) @@ -1075,6 +1169,7 @@ func (w *Walker) emitFeature(feature string) { } panic("feature contains newlines: " + f) } + w.features[f] = true if *verbose { log.Printf("feature: %s", f) diff --git a/src/cmd/api/goapi_test.go b/src/cmd/api/goapi_test.go index c7cc601b1..1a86c0ec7 100644 --- a/src/cmd/api/goapi_test.go +++ b/src/cmd/api/goapi_test.go @@ -5,6 +5,7 @@ package main import ( + "bytes" "flag" "fmt" "io/ioutil" @@ -73,3 +74,68 @@ func TestGolden(t *testing.T) { } } } + +func TestCompareAPI(t *testing.T) { + tests := []struct { + name string + features, required, optional, exception []string + ok bool // want + out string // want + }{ + { + name: "feature added", + features: []string{"A", "B", "C", "D", "E", "F"}, + required: []string{"B", "D"}, + ok: true, + out: "+A\n+C\n+E\n+F\n", + }, + { + name: "feature removed", + features: []string{"C", "A"}, + required: []string{"A", "B", "C"}, + ok: false, + out: "-B\n", + }, + { + name: "feature added then removed", + features: []string{"A", "C"}, + optional: []string{"B"}, + required: []string{"A", "C"}, + ok: true, + out: "±B\n", + }, + { + name: "exception removal", + required: []string{"A", "B", "C"}, + features: []string{"A", "C"}, + exception: []string{"B"}, + ok: true, + out: "~B\n", + }, + { + // http://golang.org/issue/4303 + name: "contexts reconverging", + required: []string{ + "A", + "pkg syscall (darwin-386), type RawSockaddrInet6 struct", + "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct", + }, + features: []string{ + "A", + "pkg syscall, type RawSockaddrInet6 struct", + }, + ok: true, + out: "+pkg syscall, type RawSockaddrInet6 struct\n", + }, + } + for _, tt := range tests { + buf := new(bytes.Buffer) + gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception) + if gotok != tt.ok { + t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok) + } + if got := buf.String(); got != tt.out { + t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out) + } + } +} diff --git a/src/cmd/api/testdata/src/pkg/p1/golden.txt b/src/cmd/api/testdata/src/pkg/p1/golden.txt index e334e5776..abcc0ce6c 100644 --- a/src/cmd/api/testdata/src/pkg/p1/golden.txt +++ b/src/cmd/api/testdata/src/pkg/p1/golden.txt @@ -10,6 +10,7 @@ pkg p1, func Bar(int8, int16, int64) pkg p1, func Bar1(int8, int16, int64) uint64 pkg p1, func Bar2(int8, int16, int64) (uint8, uint64) pkg p1, func BarE() Error +pkg p1, func PlainFunc(int, int, string) (*B, error) pkg p1, func TakesFunc(func(int) int) pkg p1, method (*B) JustOnB() pkg p1, method (*B) OnBothTandBPtr() @@ -27,6 +28,9 @@ pkg p1, method (TPtrExported) OnEmbedded() pkg p1, method (TPtrUnexported) OnBothTandBPtr() pkg p1, method (TPtrUnexported) OnBothTandBVal() pkg p1, type B struct +pkg p1, type ByteStruct struct +pkg p1, type ByteStruct struct, B uint8 +pkg p1, type ByteStruct struct, R int32 pkg p1, type Codec struct pkg p1, type Codec struct, Func func(int, int) int pkg p1, type EmbedSelector struct @@ -37,15 +41,21 @@ pkg p1, type Embedded struct pkg p1, type Error interface { Error, Temporary } pkg p1, type Error interface, Error() string pkg p1, type Error interface, Temporary() bool -pkg p1, type I interface { Get, GetNamed, Name, PackageTwoMeth, Set } +pkg p1, type FuncType func(int, int, string) (*B, error) pkg p1, type I interface, Get(string) int64 pkg p1, type I interface, GetNamed(string) int64 pkg p1, type I interface, Name() string pkg p1, type I interface, PackageTwoMeth() pkg p1, type I interface, Set(string, int64) +pkg p1, type I interface, unexported methods pkg p1, type MyInt int pkg p1, type Namer interface { Name } pkg p1, type Namer interface, Name() string +pkg p1, type Private interface, X() +pkg p1, type Private interface, unexported methods +pkg p1, type Public interface { X, Y } +pkg p1, type Public interface, X() +pkg p1, type Public interface, Y() pkg p1, type S struct pkg p1, type S struct, Public *int pkg p1, type S struct, PublicTime time.Time @@ -58,7 +68,9 @@ pkg p1, type T struct pkg p1, type TPtrExported struct pkg p1, type TPtrExported struct, embedded *Embedded pkg p1, type TPtrUnexported struct -pkg p1, var ByteConv []byte +pkg p1, var Byte uint8 +pkg p1, var ByteConv []uint8 +pkg p1, var ByteFunc func(uint8) int32 pkg p1, var ChecksumError error pkg p1, var SIPtr *SI pkg p1, var SIPtr2 *SI diff --git a/src/cmd/api/testdata/src/pkg/p1/p1.go b/src/cmd/api/testdata/src/pkg/p1/p1.go index d965bb75e..f94c9ceeb 100644 --- a/src/cmd/api/testdata/src/pkg/p1/p1.go +++ b/src/cmd/api/testdata/src/pkg/p1/p1.go @@ -78,6 +78,16 @@ type I interface { private() } +type Public interface { + X() + Y() +} + +type Private interface { + X() + y() +} + type Error interface { error Temporary() bool @@ -139,8 +149,12 @@ type TPtrExported struct { *Embedded } +type FuncType func(x, y int, s string) (b *B, err error) + type Embedded struct{} +func PlainFunc(x, y int, s string) (b *B, err error) + func (*Embedded) OnEmbedded() {} func (*T) JustOnT() {} @@ -151,3 +165,39 @@ func (common) OnBothTandBVal() {} type EmbedSelector struct { time.Time } + +const ( + foo = "foo" + foo2 string = "foo2" + truth = foo == "foo" || foo2 == "foo2" +) + +func ellipsis(...string) {} + +var x = &S{ + Public: nil, + private: nil, + publicTime: time.Now(), +} + +var parenExpr = (1 + 5) + +var funcLit = func() {} + +var m map[string]int + +var chanVar chan int + +var ifaceVar interface{} = 5 + +var assertVar = ifaceVar.(int) + +var indexVar = m["foo"] + +var Byte byte +var ByteFunc func(byte) rune + +type ByteStruct struct { + B byte + R rune +} diff --git a/src/cmd/api/testdata/src/pkg/p3/golden.txt b/src/cmd/api/testdata/src/pkg/p3/golden.txt new file mode 100644 index 000000000..a7dcccd1b --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/golden.txt @@ -0,0 +1,3 @@ +pkg p3, func BadHop(int, int, int) (bool, bool, *ThirdBase, *ThirdBase, error) +pkg p3, method (*ThirdBase) GoodPlayer() (int, int, int) +pkg p3, type ThirdBase struct diff --git a/src/cmd/api/testdata/src/pkg/p3/p3.go b/src/cmd/api/testdata/src/pkg/p3/p3.go new file mode 100644 index 000000000..1b2b1a426 --- /dev/null +++ b/src/cmd/api/testdata/src/pkg/p3/p3.go @@ -0,0 +1,6 @@ +package p3 + +type ThirdBase struct{} + +func (tb *ThirdBase) GoodPlayer() (i, j, k int) +func BadHop(i, j, k int) (l, m bool, n, o *ThirdBase, err error) diff --git a/src/cmd/cc/Makefile b/src/cmd/cc/Makefile index 109578297..34df31d6f 100644 --- a/src/cmd/cc/Makefile +++ b/src/cmd/cc/Makefile @@ -7,4 +7,4 @@ include ../../Make.dist install: y.tab.h y.tab.h: cc.y - LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y a.y + LANG=C LANGUAGE=en_US.UTF8 bison -d -v -y cc.y diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 4c527a2b3..866aeb0ab 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -300,6 +300,7 @@ enum OPOSTINC, OPREDEC, OPREINC, + OPREFETCH, OPROTO, OREGISTER, ORETURN, @@ -324,6 +325,7 @@ enum OINDEX, OFAS, OREGPAIR, + OROTL, OEND }; @@ -442,25 +444,6 @@ struct Funct Sym* castfr[NTYPE]; }; -struct Dynimp -{ - char* local; - char* remote; - char* path; -}; - -EXTERN Dynimp *dynimp; -EXTERN int ndynimp; - -struct Dynexp -{ - char* local; - char* remote; -}; - -EXTERN Dynexp *dynexp; -EXTERN int ndynexp; - EXTERN struct { Type* tenum; /* type of entire enum */ @@ -473,7 +456,7 @@ EXTERN int autobn; EXTERN int32 autoffset; EXTERN int blockno; EXTERN Decl* dclstack; -EXTERN char debug[256]; +EXTERN int debug[256]; EXTERN Hist* ehist; EXTERN int32 firstbit; EXTERN Sym* firstarg; @@ -526,10 +509,12 @@ EXTERN int packflg; EXTERN int fproundflg; EXTERN int textflag; EXTERN int dataflag; +EXTERN int flag_largemodel; EXTERN int ncontin; EXTERN int canreach; EXTERN int warnreach; EXTERN Bits zbits; +EXTERN Fmt pragcgobuf; extern char *onames[], *tnames[], *gnames[]; extern char *cnames[], *qnames[], *bnames[]; @@ -771,8 +756,7 @@ void pragfpround(void); void pragdataflag(void); void pragtextflag(void); void pragincomplete(void); -void pragdynimport(void); -void pragdynexport(void); +void pragcgo(char*); /* * calls to machine depend part diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y index 515a80372..830dd21f8 100644 --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -93,7 +93,7 @@ %token <sval> LSTRING LLSTRING %token LAUTO LBREAK LCASE LCHAR LCONTINUE LDEFAULT LDO %token LDOUBLE LELSE LEXTERN LFLOAT LFOR LGOTO -%token LIF LINT LLONG LREGISTER LRETURN LSHORT LSIZEOF LUSED +%token LIF LINT LLONG LPREFETCH LREGISTER LRETURN LSHORT LSIZEOF LUSED %token LSTATIC LSTRUCT LSWITCH LTYPEDEF LTYPESTR LUNION LUNSIGNED %token LWHILE LVOID LENUM LSIGNED LCONSTNT LVOLATILE LSET LSIGNOF %token LRESTRICT LINLINE @@ -535,6 +535,10 @@ ulstmnt: { $$ = new(OUSED, $3, Z); } +| LPREFETCH '(' zelist ')' ';' + { + $$ = new(OPREFETCH, $3, Z); + } | LSET '(' zelist ')' ';' { $$ = new(OSET, $3, Z); diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index a3ed9772d..edfc7e75a 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -1058,6 +1058,10 @@ sigind(Type *t, Typetab *tt) return p-a; if((n&15) == 0){ na = malloc((n+16)*sizeof(Type*)); + if(na == nil) { + print("%s: out of memory", argv0); + errorexit(); + } memmove(na, a, n*sizeof(Type*)); free(a); a = tt->a = na; diff --git a/src/cmd/cc/doc.go b/src/cmd/cc/doc.go index 51aa8b192..10901b441 100644 --- a/src/cmd/cc/doc.go +++ b/src/cmd/cc/doc.go @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// +build ignore + /* This directory contains the portable section of the Plan 9 C compilers. See ../6c, ../8c, and ../5c for more information. */ -package documentation +package main diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index c579e20d9..2f038f520 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -662,65 +662,93 @@ getimpsym(void) return lookup(); } -void -pragdynimport(void) +static int +more(void) { - Sym *local, *remote; - char *path; - Dynimp *f; - - local = getimpsym(); - if(local == nil) - goto err; - - remote = getimpsym(); - if(remote == nil) - goto err; - - path = getquoted(); - if(path == nil) - goto err; - - if(ndynimp%32 == 0) - dynimp = realloc(dynimp, (ndynimp+32)*sizeof dynimp[0]); - f = &dynimp[ndynimp++]; - f->local = local->name; - f->remote = remote->name; - f->path = path; - goto out; - -err: - yyerror("usage: #pragma dynimport local remote \"path\""); - -out: - while(getnsc() != '\n') - ; + int c; + + do + c = getnsc(); + while(c == ' ' || c == '\t'); + unget(c); + return c != '\n'; } void -pragdynexport(void) +pragcgo(char *verb) { Sym *local, *remote; - Dynexp *f; - - local = getsym(); - if(local == nil) - goto err; - - remote = getsym(); - if(remote == nil) - goto err; + char *p; - if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); - f = &dynexp[ndynexp++]; - f->local = local->name; - f->remote = remote->name; - goto out; - -err: - yyerror("usage: #pragma dynexport local remote"); + if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) { + p = getquoted(); + if(p == nil) + goto err1; + fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p); + goto out; + + err1: + yyerror("usage: #pragma cgo_dynamic_linker \"path\""); + goto out; + } + + if(strcmp(verb, "cgo_export") == 0 || strcmp(verb, "dynexport") == 0) { + local = getimpsym(); + if(local == nil) + goto err2; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_export %q\n", local->name); + goto out; + } + remote = getimpsym(); + if(remote == nil) + goto err2; + fmtprint(&pragcgobuf, "cgo_export %q %q\n", local->name, remote->name); + goto out; + + err2: + yyerror("usage: #pragma cgo_export local [remote]"); + goto out; + } + + if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) { + local = getimpsym(); + if(local == nil) + goto err3; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local->name); + goto out; + } + remote = getimpsym(); + if(remote == nil) + goto err3; + if(!more()) { + fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local->name, remote->name); + goto out; + } + p = getquoted(); + if(p == nil) + goto err3; + fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local->name, remote->name, p); + goto out; + + err3: + yyerror("usage: #pragma cgo_import_dynamic local [remote [\"library\"]]"); + goto out; + } + + if(strcmp(verb, "cgo_import_static") == 0) { + local = getimpsym(); + if(local == nil) + goto err4; + fmtprint(&pragcgobuf, "cgo_import_static %q\n", local->name); + goto out; + err4: + yyerror("usage: #pragma cgo_import_static local [remote]"); + goto out; + } + out: while(getnsc() != '\n') ; diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c index 4274c5626..7457bd000 100644 --- a/src/cmd/cc/godefs.c +++ b/src/cmd/cc/godefs.c @@ -188,10 +188,10 @@ printtypename(Type *t) switch(t->etype) { case TINT: - Bprint(&outbuf, "int"); + Bprint(&outbuf, "int32"); break; case TUINT: - Bprint(&outbuf, "uint"); + Bprint(&outbuf, "uint32"); break; case TCHAR: Bprint(&outbuf, "int8"); diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 8aeb1a334..4fb0be9a4 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -86,10 +86,37 @@ pathchar(void) */ void +usage(void) +{ + print("usage: %cc [options] file.c...\n", thechar); + flagprint(1); + errorexit(); +} + +void +dospim(void) +{ + thechar = '0'; + thestring = "spim"; +} + +char **defs; +int ndef; + +void +dodef(char *p) +{ + if(ndef%8 == 0) + defs = allocn(defs, ndef*sizeof(char *), + 8*sizeof(char *)); + defs[ndef++] = p; + dodefine(p); +} + +void main(int argc, char *argv[]) { - char **defs, *p; - int c, ndef; + int c; ensuresymb(NSYMB); memset(debug, 0, sizeof(debug)); @@ -97,52 +124,67 @@ main(int argc, char *argv[]) cinit(); ginit(); arginit(); + + fmtstrinit(&pragcgobuf); + quotefmtinstall(); tufield = simplet((1L<<tfield->etype) | BUNSIGNED); ndef = 0; defs = nil; outfile = 0; setinclude("."); - ARGBEGIN { - default: - c = ARGC(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - - case 'l': /* for little-endian mips */ - if(thechar != 'v'){ - print("can only use -l with vc\n"); - errorexit(); - } - thechar = '0'; - thestring = "spim"; - break; - - case 'o': - outfile = ARGF(); - break; - case 'D': - p = ARGF(); - if(p) { - if(ndef%8 == 0) - defs = allocn(defs, ndef*sizeof(char *), - 8*sizeof(char *)); - defs[ndef++] = p; - dodefine(p); - } - break; + flagcount("+", "pass -+ to preprocessor", &debug['+']); + flagcount(".", "pass -. to preprocessor", &debug['.']); + flagcount("<", "debug shift", &debug['<']); + flagcount("A", "debug alignment", &debug['A']); + flagcount("B", "allow pre-ANSI code", &debug['B']); + if(thechar == '5') + flagcount("C", "debug constant propagation", &debug['C']); + flagfn1("D", "name[=value]: add #define", dodef); + flagcount("F", "enable print format checks", &debug['F']); + if(thechar == '5') + flagcount("H", "debug shift propagation", &debug['H']); + flagfn1("I", "dir: add dir to include path", setinclude); + flagcount("L", "debug lexer", &debug['L']); + flagcount("M", "debug move generation", &debug['M']); + flagcount("N", "disable optimizations", &debug['N']); + flagcount("P", "debug peephole optimizer", &debug['P']); + flagcount("Q", "print exported Go definitions", &debug['Q']); + flagcount("R", "debug register optimizer", &debug['R']); + flagcount("S", "print assembly", &debug['S']); + flagcount("T", "enable type signatures", &debug['T']); + flagcount("V", "enable pointer type checks", &debug['V']); + flagcount("W", "debug switch generation", &debug['W']); + flagcount("X", "abort on error", &debug['X']); + flagcount("Y", "debug index generation", &debug['Y']); + flagcount("Z", "skip code generation", &debug['Z']); + flagcount("a", "print acid definitions", &debug['a']); + flagcount("c", "debug constant evaluation", &debug['c']); + flagcount("d", "debug declarations", &debug['d']); + flagcount("e", "debug macro expansion", &debug['e']); + flagcount("f", "debug pragmas", &debug['f']); + flagcount("g", "debug code generation", &debug['g']); + flagcount("i", "debug initialization", &debug['i']); + if(thechar == 'v') + flagfn0("l", "little-endian mips mode", dospim); + flagcount("m", "debug multiplication", &debug['m']); + flagcount("n", "print acid/Go to file, not stdout", &debug['n']); + flagstr("o", "file: set output file", &outfile); + flagcount("p", "invoke C preprocessor", &debug['p']); + flagcount("q", "print Go definitions", &debug['q']); + flagcount("s", "print #define assembly offsets", &debug['s']); + flagcount("t", "debug code generation", &debug['t']); + flagcount("w", "enable warnings", &debug['w']); + flagcount("v", "increase debug verbosity", &debug['v']); + if(thechar == '6') + flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); + + flagparse(&argc, &argv, usage); + + if(argc < 1 && outfile == 0) + usage(); - case 'I': - p = ARGF(); - setinclude(p); - break; - } ARGEND - if(argc < 1 && outfile == 0) { - print("usage: %cc [-options] files\n", thechar); - errorexit(); - } if(argc > 1){ print("can't compile multiple files\n"); errorexit(); @@ -377,11 +419,16 @@ lookup(void) symb[1] = '"'; } - // turn · into . for(r=w=symb; *r; r++) { + // turn · (U+00B7) into . + // turn ∕ (U+2215) into / if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { *w++ = '.'; r++; + }else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) { + *w++ = '/'; + r++; + r++; }else *w++ = *r; } @@ -1174,6 +1221,7 @@ struct "inline", LINLINE, 0, "int", LINT, TINT, "long", LLONG, TLONG, + "PREFETCH", LPREFETCH, 0, "register", LREGISTER, 0, "restrict", LRESTRICT, 0, "return", LRETURN, 0, diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index d339cf9a2..5fa980267 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -47,15 +47,9 @@ pragvararg(void) } void -pragdynimport(void) -{ - while(getnsc() != '\n') - ; -} - -void -pragdynexport(void) +pragcgo(char *name) { + USED(name); while(getnsc() != '\n') ; } @@ -244,11 +238,16 @@ lookup(void) symb[1] = '"'; } - // turn · into . for(r=w=symb; *r; r++) { + // turn · (U+00B7) into . + // turn ∕ (U+2215) into / if((uchar)*r == 0xc2 && (uchar)*(r+1) == 0xb7) { *w++ = '.'; r++; + }else if((uchar)*r == 0xe2 && (uchar)*(r+1) == 0x88 && (uchar)*(r+2) == 0x95) { + *w++ = '/'; + r++; + r++; }else *w++ = *r; } diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index 874e82d25..f0a507669 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -743,12 +743,8 @@ macprag(void) pragincomplete(); return; } - if(s && strcmp(s->name, "dynimport") == 0) { - pragdynimport(); - return; - } - if(s && strcmp(s->name, "dynexport") == 0) { - pragdynexport(); + if(s && (strncmp(s->name, "cgo_", 4) == 0 || strncmp(s->name, "dyn", 3) == 0)) { + pragcgo(s->name); return; } while(getnsc() != '\n') diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index 3a686102f..b06aa996d 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -528,6 +528,7 @@ loop: case OSET: case OUSED: + case OPREFETCH: usedset(n->left, o); break; } @@ -542,6 +543,10 @@ usedset(Node *n, int o) return; } complex(n); + if(o == OPREFETCH) { + gprefetch(n); + return; + } switch(n->op) { case OADDR: /* volatile */ gins(ANOP, n, Z); diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index 72d671b2f..3a5576385 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -847,12 +847,6 @@ simplifyshift(Node *n) c2 = n->left->left->right->vconst; c3 = n->left->right->vconst; -/* - if(debug['h']) - print("%.3o %d %d %d #%.ux\n", - (s1<<3)|s2, c1, c2, topbit(c3), c3); -*/ - o = n->op; switch((s1<<3)|s2) { case 000: /* (((e <<u c2) & c3) <<u c1) */ @@ -1497,6 +1491,7 @@ Init onamesinit[] = OPOSTINC, 0, "POSTINC", OPREDEC, 0, "PREDEC", OPREINC, 0, "PREINC", + OPREFETCH, 0, "PREFETCH", OPROTO, 0, "PROTO", OREGISTER, 0, "REGISTER", ORETURN, 0, "RETURN", @@ -1520,6 +1515,7 @@ Init onamesinit[] = OINDEX, 0, "INDEX", OFAS, 0, "FAS", OREGPAIR, 0, "REGPAIR", + OROTL, 0, "ROTL", OEND, 0, "END", -1, 0, 0, }; diff --git a/src/cmd/cc/y.tab.c b/src/cmd/cc/y.tab.c index af8328abd..10938c10b 100644 --- a/src/cmd/cc/y.tab.c +++ b/src/cmd/cc/y.tab.c @@ -115,28 +115,29 @@ LIF = 304, LINT = 305, LLONG = 306, - LREGISTER = 307, - LRETURN = 308, - LSHORT = 309, - LSIZEOF = 310, - LUSED = 311, - LSTATIC = 312, - LSTRUCT = 313, - LSWITCH = 314, - LTYPEDEF = 315, - LTYPESTR = 316, - LUNION = 317, - LUNSIGNED = 318, - LWHILE = 319, - LVOID = 320, - LENUM = 321, - LSIGNED = 322, - LCONSTNT = 323, - LVOLATILE = 324, - LSET = 325, - LSIGNOF = 326, - LRESTRICT = 327, - LINLINE = 328 + LPREFETCH = 307, + LREGISTER = 308, + LRETURN = 309, + LSHORT = 310, + LSIZEOF = 311, + LUSED = 312, + LSTATIC = 313, + LSTRUCT = 314, + LSWITCH = 315, + LTYPEDEF = 316, + LTYPESTR = 317, + LUNION = 318, + LUNSIGNED = 319, + LWHILE = 320, + LVOID = 321, + LENUM = 322, + LSIGNED = 323, + LCONSTNT = 324, + LVOLATILE = 325, + LSET = 326, + LSIGNOF = 327, + LRESTRICT = 328, + LINLINE = 329 }; #endif /* Tokens. */ @@ -189,28 +190,29 @@ #define LIF 304 #define LINT 305 #define LLONG 306 -#define LREGISTER 307 -#define LRETURN 308 -#define LSHORT 309 -#define LSIZEOF 310 -#define LUSED 311 -#define LSTATIC 312 -#define LSTRUCT 313 -#define LSWITCH 314 -#define LTYPEDEF 315 -#define LTYPESTR 316 -#define LUNION 317 -#define LUNSIGNED 318 -#define LWHILE 319 -#define LVOID 320 -#define LENUM 321 -#define LSIGNED 322 -#define LCONSTNT 323 -#define LVOLATILE 324 -#define LSET 325 -#define LSIGNOF 326 -#define LRESTRICT 327 -#define LINLINE 328 +#define LPREFETCH 307 +#define LREGISTER 308 +#define LRETURN 309 +#define LSHORT 310 +#define LSIZEOF 311 +#define LUSED 312 +#define LSTATIC 313 +#define LSTRUCT 314 +#define LSWITCH 315 +#define LTYPEDEF 316 +#define LTYPESTR 317 +#define LUNION 318 +#define LUNSIGNED 319 +#define LWHILE 320 +#define LVOID 321 +#define LENUM 322 +#define LSIGNED 323 +#define LCONSTNT 324 +#define LVOLATILE 325 +#define LSET 326 +#define LSIGNOF 327 +#define LRESTRICT 328 +#define LINLINE 329 @@ -270,7 +272,7 @@ typedef union YYSTYPE vlong vval; } /* Line 193 of yacc.c. */ -#line 274 "y.tab.c" +#line 276 "y.tab.c" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 @@ -283,7 +285,7 @@ typedef union YYSTYPE /* Line 216 of yacc.c. */ -#line 287 "y.tab.c" +#line 289 "y.tab.c" #ifdef short # undef short @@ -501,17 +503,17 @@ union yyalloc #define YYLAST 1188 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 98 +#define YYNTOKENS 99 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 75 /* YYNRULES -- Number of rules. */ -#define YYNRULES 246 +#define YYNRULES 247 /* YYNRULES -- Number of states. */ -#define YYNSTATES 412 +#define YYNSTATES 417 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 328 +#define YYMAXUTOK 329 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -522,16 +524,16 @@ static const yytype_uint8 yytranslate[] = 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 96, 2, 2, 2, 35, 22, 2, - 38, 92, 33, 31, 4, 32, 36, 34, 2, 2, + 2, 2, 2, 97, 2, 2, 2, 35, 22, 2, + 38, 93, 33, 31, 4, 32, 36, 34, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 3, 25, 5, 26, 16, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 37, 2, 93, 21, 2, 2, 2, 2, 2, + 2, 37, 2, 94, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 94, 20, 95, 97, 2, 2, 2, + 2, 2, 2, 95, 20, 96, 98, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -551,7 +553,7 @@ static const yytype_uint8 yytranslate[] = 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 + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92 }; #if YYDEBUG @@ -569,102 +571,103 @@ static const yytype_uint16 yyprhs[] = 208, 212, 213, 216, 219, 221, 224, 228, 231, 234, 237, 239, 242, 244, 247, 250, 251, 254, 260, 268, 269, 280, 286, 294, 298, 304, 307, 310, 314, 320, - 326, 327, 329, 330, 332, 334, 336, 340, 342, 346, - 350, 354, 358, 362, 366, 370, 374, 378, 382, 386, - 390, 394, 398, 402, 406, 410, 414, 420, 424, 428, - 432, 436, 440, 444, 448, 452, 456, 460, 464, 466, - 472, 480, 482, 485, 488, 491, 494, 497, 500, 503, - 506, 509, 512, 516, 522, 528, 533, 538, 542, 546, - 549, 552, 554, 556, 558, 560, 562, 564, 566, 568, - 570, 572, 574, 576, 579, 581, 584, 585, 587, 589, - 593, 594, 599, 600, 602, 604, 606, 608, 611, 614, - 618, 621, 625, 627, 629, 632, 633, 638, 641, 644, - 645, 650, 653, 656, 657, 658, 666, 667, 673, 675, - 677, 680, 681, 684, 686, 688, 690, 692, 695, 697, - 699, 701, 705, 708, 712, 714, 716, 718, 720, 722, - 724, 726, 728, 730, 732, 734, 736, 738, 740, 742, - 744, 746, 748, 750, 752, 754, 756 + 326, 332, 333, 335, 336, 338, 340, 342, 346, 348, + 352, 356, 360, 364, 368, 372, 376, 380, 384, 388, + 392, 396, 400, 404, 408, 412, 416, 420, 426, 430, + 434, 438, 442, 446, 450, 454, 458, 462, 466, 470, + 472, 478, 486, 488, 491, 494, 497, 500, 503, 506, + 509, 512, 515, 518, 522, 528, 534, 539, 544, 548, + 552, 555, 558, 560, 562, 564, 566, 568, 570, 572, + 574, 576, 578, 580, 582, 585, 587, 590, 591, 593, + 595, 599, 600, 605, 606, 608, 610, 612, 614, 617, + 620, 624, 627, 631, 633, 635, 638, 639, 644, 647, + 650, 651, 656, 659, 662, 663, 664, 672, 673, 679, + 681, 683, 686, 687, 690, 692, 694, 696, 698, 701, + 703, 705, 707, 711, 714, 718, 720, 722, 724, 726, + 728, 730, 732, 734, 736, 738, 740, 742, 744, 746, + 748, 750, 752, 754, 756, 758, 760, 762 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { - 99, 0, -1, -1, 99, 100, -1, 151, 3, -1, - 151, 103, 3, -1, -1, -1, 151, 105, 101, 110, - 102, 128, -1, 105, -1, -1, 105, 104, 5, 122, - -1, 103, 4, 103, -1, 106, -1, 33, 162, 105, - -1, 171, -1, 38, 105, 92, -1, 106, 38, 126, - 92, -1, 106, 37, 138, 93, -1, 154, 3, -1, - 154, 108, 3, -1, 105, -1, -1, 105, 109, 5, - 122, -1, 108, 4, 108, -1, -1, 110, 154, 111, - 3, -1, 105, -1, 111, 4, 111, -1, -1, 153, - 113, 115, 3, -1, -1, 112, 153, 114, 115, 3, - -1, -1, 116, -1, 117, -1, 116, 4, 116, -1, - 105, -1, 171, 17, 139, -1, 17, 139, -1, -1, - 119, -1, 33, 162, -1, 33, 162, 119, -1, 120, - -1, 121, -1, 120, 38, 126, 92, -1, 120, 37, - 138, 93, -1, 38, 92, -1, 37, 138, 93, -1, - 38, 119, 92, -1, 141, -1, 94, 125, 95, -1, - 37, 139, 93, -1, 36, 172, -1, 123, 5, -1, - 122, 4, -1, 124, 122, 4, -1, 123, -1, 124, - 123, -1, 124, -1, 122, -1, 124, 122, -1, -1, - 127, -1, 170, -1, 153, 118, -1, 153, 105, -1, - 36, 36, 36, -1, 127, 4, 127, -1, 94, 129, - 95, -1, -1, 129, 107, -1, 129, 132, -1, 131, - -1, 130, 131, -1, 56, 141, 17, -1, 59, 17, - -1, 42, 17, -1, 1, 3, -1, 134, -1, 130, - 134, -1, 137, -1, 154, 108, -1, 137, 3, -1, - -1, 135, 128, -1, 67, 38, 140, 92, 132, -1, - 67, 38, 140, 92, 132, 62, 132, -1, -1, 136, - 65, 38, 133, 3, 137, 3, 137, 92, 132, -1, - 82, 38, 140, 92, 132, -1, 60, 132, 82, 38, - 140, 92, 3, -1, 71, 137, 3, -1, 77, 38, - 140, 92, 132, -1, 55, 3, -1, 58, 3, -1, - 66, 172, 3, -1, 74, 38, 147, 92, 3, -1, - 88, 38, 147, 92, 3, -1, -1, 140, -1, -1, - 139, -1, 141, -1, 141, -1, 140, 4, 140, -1, - 142, -1, 141, 33, 141, -1, 141, 34, 141, -1, - 141, 35, 141, -1, 141, 31, 141, -1, 141, 32, - 141, -1, 141, 29, 141, -1, 141, 30, 141, -1, - 141, 25, 141, -1, 141, 26, 141, -1, 141, 28, - 141, -1, 141, 27, 141, -1, 141, 24, 141, -1, - 141, 23, 141, -1, 141, 22, 141, -1, 141, 21, - 141, -1, 141, 20, 141, -1, 141, 19, 141, -1, - 141, 18, 141, -1, 141, 16, 140, 17, 141, -1, - 141, 5, 141, -1, 141, 15, 141, -1, 141, 14, - 141, -1, 141, 13, 141, -1, 141, 12, 141, -1, - 141, 11, 141, -1, 141, 9, 141, -1, 141, 10, - 141, -1, 141, 8, 141, -1, 141, 7, 141, -1, - 141, 6, 141, -1, 143, -1, 38, 153, 118, 92, - 142, -1, 38, 153, 118, 92, 94, 125, 95, -1, - 144, -1, 33, 142, -1, 22, 142, -1, 31, 142, - -1, 32, 142, -1, 96, 142, -1, 97, 142, -1, - 40, 142, -1, 41, 142, -1, 73, 143, -1, 89, - 143, -1, 38, 140, 92, -1, 73, 38, 153, 118, - 92, -1, 89, 38, 153, 118, 92, -1, 144, 38, - 147, 92, -1, 144, 37, 140, 93, -1, 144, 39, - 172, -1, 144, 36, 172, -1, 144, 40, -1, 144, - 41, -1, 170, -1, 46, -1, 47, -1, 48, -1, - 49, -1, 45, -1, 44, -1, 50, -1, 51, -1, - 145, -1, 146, -1, 52, -1, 145, 52, -1, 53, - -1, 146, 53, -1, -1, 148, -1, 141, -1, 148, - 4, 148, -1, -1, 94, 150, 112, 95, -1, -1, - 154, -1, 155, -1, 167, -1, 164, -1, 155, 161, - -1, 167, 161, -1, 164, 155, 162, -1, 164, 167, - -1, 164, 167, 161, -1, 152, -1, 152, -1, 76, - 172, -1, -1, 76, 172, 156, 149, -1, 76, 149, - -1, 80, 172, -1, -1, 80, 172, 157, 149, -1, - 80, 149, -1, 84, 172, -1, -1, -1, 84, 172, - 158, 94, 159, 166, 95, -1, -1, 84, 94, 160, - 166, 95, -1, 43, -1, 163, -1, 161, 163, -1, - -1, 162, 169, -1, 167, -1, 169, -1, 168, -1, - 165, -1, 164, 165, -1, 169, -1, 168, -1, 42, - -1, 42, 5, 141, -1, 166, 4, -1, 166, 4, - 166, -1, 57, -1, 72, -1, 68, -1, 69, -1, - 85, -1, 81, -1, 64, -1, 61, -1, 83, -1, - 54, -1, 75, -1, 63, -1, 78, -1, 79, -1, - 70, -1, 91, -1, 86, -1, 87, -1, 90, -1, - 42, -1, 172, -1, 42, -1, 43, -1 + 100, 0, -1, -1, 100, 101, -1, 152, 3, -1, + 152, 104, 3, -1, -1, -1, 152, 106, 102, 111, + 103, 129, -1, 106, -1, -1, 106, 105, 5, 123, + -1, 104, 4, 104, -1, 107, -1, 33, 163, 106, + -1, 172, -1, 38, 106, 93, -1, 107, 38, 127, + 93, -1, 107, 37, 139, 94, -1, 155, 3, -1, + 155, 109, 3, -1, 106, -1, -1, 106, 110, 5, + 123, -1, 109, 4, 109, -1, -1, 111, 155, 112, + 3, -1, 106, -1, 112, 4, 112, -1, -1, 154, + 114, 116, 3, -1, -1, 113, 154, 115, 116, 3, + -1, -1, 117, -1, 118, -1, 117, 4, 117, -1, + 106, -1, 172, 17, 140, -1, 17, 140, -1, -1, + 120, -1, 33, 163, -1, 33, 163, 120, -1, 121, + -1, 122, -1, 121, 38, 127, 93, -1, 121, 37, + 139, 94, -1, 38, 93, -1, 37, 139, 94, -1, + 38, 120, 93, -1, 142, -1, 95, 126, 96, -1, + 37, 140, 94, -1, 36, 173, -1, 124, 5, -1, + 123, 4, -1, 125, 123, 4, -1, 124, -1, 125, + 124, -1, 125, -1, 123, -1, 125, 123, -1, -1, + 128, -1, 171, -1, 154, 119, -1, 154, 106, -1, + 36, 36, 36, -1, 128, 4, 128, -1, 95, 130, + 96, -1, -1, 130, 108, -1, 130, 133, -1, 132, + -1, 131, 132, -1, 56, 142, 17, -1, 59, 17, + -1, 42, 17, -1, 1, 3, -1, 135, -1, 131, + 135, -1, 138, -1, 155, 109, -1, 138, 3, -1, + -1, 136, 129, -1, 67, 38, 141, 93, 133, -1, + 67, 38, 141, 93, 133, 62, 133, -1, -1, 137, + 65, 38, 134, 3, 138, 3, 138, 93, 133, -1, + 83, 38, 141, 93, 133, -1, 60, 133, 83, 38, + 141, 93, 3, -1, 72, 138, 3, -1, 78, 38, + 141, 93, 133, -1, 55, 3, -1, 58, 3, -1, + 66, 173, 3, -1, 75, 38, 148, 93, 3, -1, + 70, 38, 148, 93, 3, -1, 89, 38, 148, 93, + 3, -1, -1, 141, -1, -1, 140, -1, 142, -1, + 142, -1, 141, 4, 141, -1, 143, -1, 142, 33, + 142, -1, 142, 34, 142, -1, 142, 35, 142, -1, + 142, 31, 142, -1, 142, 32, 142, -1, 142, 29, + 142, -1, 142, 30, 142, -1, 142, 25, 142, -1, + 142, 26, 142, -1, 142, 28, 142, -1, 142, 27, + 142, -1, 142, 24, 142, -1, 142, 23, 142, -1, + 142, 22, 142, -1, 142, 21, 142, -1, 142, 20, + 142, -1, 142, 19, 142, -1, 142, 18, 142, -1, + 142, 16, 141, 17, 142, -1, 142, 5, 142, -1, + 142, 15, 142, -1, 142, 14, 142, -1, 142, 13, + 142, -1, 142, 12, 142, -1, 142, 11, 142, -1, + 142, 9, 142, -1, 142, 10, 142, -1, 142, 8, + 142, -1, 142, 7, 142, -1, 142, 6, 142, -1, + 144, -1, 38, 154, 119, 93, 143, -1, 38, 154, + 119, 93, 95, 126, 96, -1, 145, -1, 33, 143, + -1, 22, 143, -1, 31, 143, -1, 32, 143, -1, + 97, 143, -1, 98, 143, -1, 40, 143, -1, 41, + 143, -1, 74, 144, -1, 90, 144, -1, 38, 141, + 93, -1, 74, 38, 154, 119, 93, -1, 90, 38, + 154, 119, 93, -1, 145, 38, 148, 93, -1, 145, + 37, 141, 94, -1, 145, 39, 173, -1, 145, 36, + 173, -1, 145, 40, -1, 145, 41, -1, 171, -1, + 46, -1, 47, -1, 48, -1, 49, -1, 45, -1, + 44, -1, 50, -1, 51, -1, 146, -1, 147, -1, + 52, -1, 146, 52, -1, 53, -1, 147, 53, -1, + -1, 149, -1, 142, -1, 149, 4, 149, -1, -1, + 95, 151, 113, 96, -1, -1, 155, -1, 156, -1, + 168, -1, 165, -1, 156, 162, -1, 168, 162, -1, + 165, 156, 163, -1, 165, 168, -1, 165, 168, 162, + -1, 153, -1, 153, -1, 77, 173, -1, -1, 77, + 173, 157, 150, -1, 77, 150, -1, 81, 173, -1, + -1, 81, 173, 158, 150, -1, 81, 150, -1, 85, + 173, -1, -1, -1, 85, 173, 159, 95, 160, 167, + 96, -1, -1, 85, 95, 161, 167, 96, -1, 43, + -1, 164, -1, 162, 164, -1, -1, 163, 170, -1, + 168, -1, 170, -1, 169, -1, 166, -1, 165, 166, + -1, 170, -1, 169, -1, 42, -1, 42, 5, 142, + -1, 167, 4, -1, 167, 4, 167, -1, 57, -1, + 73, -1, 68, -1, 69, -1, 86, -1, 82, -1, + 64, -1, 61, -1, 84, -1, 54, -1, 76, -1, + 63, -1, 79, -1, 80, -1, 71, -1, 92, -1, + 87, -1, 88, -1, 91, -1, 42, -1, 173, -1, + 42, -1, 43, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -680,21 +683,21 @@ static const yytype_uint16 yyrline[] = 396, 406, 409, 413, 419, 420, 426, 430, 434, 440, 444, 445, 451, 452, 458, 459, 459, 470, 476, 484, 484, 495, 499, 503, 508, 522, 526, 530, 534, 538, - 544, 547, 550, 553, 556, 563, 564, 570, 571, 575, + 542, 548, 551, 554, 557, 560, 567, 568, 574, 575, 579, 583, 587, 591, 595, 599, 603, 607, 611, 615, 619, 623, 627, 631, 635, 639, 643, 647, 651, 655, - 659, 663, 667, 671, 675, 679, 683, 687, 693, 694, - 701, 709, 710, 714, 718, 722, 726, 730, 734, 738, - 742, 746, 752, 756, 762, 768, 776, 780, 785, 790, - 794, 798, 799, 806, 813, 820, 827, 834, 841, 848, - 855, 856, 859, 869, 887, 897, 915, 918, 921, 922, - 929, 928, 951, 955, 958, 963, 968, 974, 982, 988, - 994, 1000, 1008, 1016, 1023, 1029, 1028, 1040, 1048, 1054, - 1053, 1065, 1073, 1082, 1086, 1081, 1103, 1102, 1111, 1117, - 1118, 1124, 1127, 1133, 1134, 1135, 1138, 1139, 1145, 1146, - 1149, 1153, 1157, 1158, 1161, 1162, 1163, 1164, 1165, 1166, - 1167, 1168, 1169, 1172, 1173, 1174, 1175, 1176, 1177, 1178, - 1181, 1182, 1183, 1186, 1201, 1213, 1214 + 659, 663, 667, 671, 675, 679, 683, 687, 691, 697, + 698, 705, 713, 714, 718, 722, 726, 730, 734, 738, + 742, 746, 750, 756, 760, 766, 772, 780, 784, 789, + 794, 798, 802, 803, 810, 817, 824, 831, 838, 845, + 852, 859, 860, 863, 873, 891, 901, 919, 922, 925, + 926, 933, 932, 955, 959, 962, 967, 972, 978, 986, + 992, 998, 1004, 1012, 1020, 1027, 1033, 1032, 1044, 1052, + 1058, 1057, 1069, 1077, 1086, 1090, 1085, 1107, 1106, 1115, + 1121, 1122, 1128, 1131, 1137, 1138, 1139, 1142, 1143, 1149, + 1150, 1153, 1157, 1161, 1162, 1165, 1166, 1167, 1168, 1169, + 1170, 1171, 1172, 1173, 1176, 1177, 1178, 1179, 1180, 1181, + 1182, 1185, 1186, 1187, 1190, 1205, 1217, 1218 }; #endif @@ -711,8 +714,8 @@ static const char *const yytname[] = "LDCONST", "LCONST", "LLCONST", "LUCONST", "LULCONST", "LVLCONST", "LUVLCONST", "LSTRING", "LLSTRING", "LAUTO", "LBREAK", "LCASE", "LCHAR", "LCONTINUE", "LDEFAULT", "LDO", "LDOUBLE", "LELSE", "LEXTERN", "LFLOAT", - "LFOR", "LGOTO", "LIF", "LINT", "LLONG", "LREGISTER", "LRETURN", - "LSHORT", "LSIZEOF", "LUSED", "LSTATIC", "LSTRUCT", "LSWITCH", + "LFOR", "LGOTO", "LIF", "LINT", "LLONG", "LPREFETCH", "LREGISTER", + "LRETURN", "LSHORT", "LSIZEOF", "LUSED", "LSTATIC", "LSTRUCT", "LSWITCH", "LTYPEDEF", "LTYPESTR", "LUNION", "LUNSIGNED", "LWHILE", "LVOID", "LENUM", "LSIGNED", "LCONSTNT", "LVOLATILE", "LSET", "LSIGNOF", "LRESTRICT", "LINLINE", "')'", "']'", "'{'", "'}'", "'!'", "'~'", @@ -743,38 +746,38 @@ static const yytype_uint16 yytoknum[] = 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, - 327, 328, 41, 93, 123, 125, 33, 126 + 327, 328, 329, 41, 93, 123, 125, 33, 126 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 98, 99, 99, 100, 100, 101, 102, 100, 103, - 104, 103, 103, 105, 105, 106, 106, 106, 106, 107, - 107, 108, 109, 108, 108, 110, 110, 111, 111, 113, - 112, 114, 112, 115, 115, 116, 116, 117, 117, 117, - 118, 118, 119, 119, 119, 120, 120, 120, 121, 121, - 121, 122, 122, 123, 123, 123, 124, 124, 124, 124, - 125, 125, 125, 126, 126, 127, 127, 127, 127, 127, - 128, 129, 129, 129, 130, 130, 131, 131, 131, 132, - 132, 132, 133, 133, 134, 135, 134, 134, 134, 136, - 134, 134, 134, 134, 134, 134, 134, 134, 134, 134, - 137, 137, 138, 138, 139, 140, 140, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, - 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 0, 99, 100, 100, 101, 101, 102, 103, 101, 104, + 105, 104, 104, 106, 106, 107, 107, 107, 107, 108, + 108, 109, 110, 109, 109, 111, 111, 112, 112, 114, + 113, 115, 113, 116, 116, 117, 117, 118, 118, 118, + 119, 119, 120, 120, 120, 121, 121, 121, 122, 122, + 122, 123, 123, 124, 124, 124, 125, 125, 125, 125, + 126, 126, 126, 127, 127, 128, 128, 128, 128, 128, + 129, 130, 130, 130, 131, 131, 132, 132, 132, 133, + 133, 133, 134, 134, 135, 136, 135, 135, 135, 137, + 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, + 135, 138, 138, 139, 139, 140, 141, 141, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, - 150, 149, 151, 151, 152, 152, 152, 152, 152, 152, - 152, 152, 153, 154, 155, 156, 155, 155, 155, 157, - 155, 155, 155, 158, 159, 155, 160, 155, 155, 161, - 161, 162, 162, 163, 163, 163, 164, 164, 165, 165, - 166, 166, 166, 166, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 168, 168, 168, 168, 168, 168, 168, - 169, 169, 169, 170, 171, 172, 172 + 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 146, 146, 147, 147, 148, 148, 149, + 149, 151, 150, 152, 152, 153, 153, 153, 153, 153, + 153, 153, 153, 154, 155, 156, 157, 156, 156, 156, + 158, 156, 156, 156, 159, 160, 156, 161, 156, 156, + 162, 162, 163, 163, 164, 164, 164, 165, 165, 166, + 166, 167, 167, 167, 167, 168, 168, 168, 168, 168, + 168, 168, 168, 168, 169, 169, 169, 169, 169, 169, + 169, 170, 170, 170, 171, 172, 173, 173 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -790,21 +793,21 @@ static const yytype_uint8 yyr2[] = 3, 0, 2, 2, 1, 2, 3, 2, 2, 2, 1, 2, 1, 2, 2, 0, 2, 5, 7, 0, 10, 5, 7, 3, 5, 2, 2, 3, 5, 5, - 0, 1, 0, 1, 1, 1, 3, 1, 3, 3, + 5, 0, 1, 0, 1, 1, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 1, 5, - 7, 1, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 3, 5, 5, 4, 4, 3, 3, 2, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 1, 2, 0, 1, 1, 3, - 0, 4, 0, 1, 1, 1, 1, 2, 2, 3, - 2, 3, 1, 1, 2, 0, 4, 2, 2, 0, - 4, 2, 2, 0, 0, 7, 0, 5, 1, 1, - 2, 0, 2, 1, 1, 1, 1, 2, 1, 1, - 1, 3, 2, 3, 1, 1, 1, 1, 1, 1, + 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, + 5, 7, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 5, 5, 4, 4, 3, 3, + 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 1, 2, 0, 1, 1, + 3, 0, 4, 0, 1, 1, 1, 1, 2, 2, + 3, 2, 3, 1, 1, 2, 0, 4, 2, 2, + 0, 4, 2, 2, 0, 0, 7, 0, 5, 1, + 1, 2, 0, 2, 1, 1, 1, 1, 2, 1, + 1, 1, 3, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1 + 1, 1, 1, 1, 1, 1, 1, 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -812,58 +815,58 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 2, 182, 1, 208, 233, 224, 231, 235, 230, 226, - 227, 238, 225, 234, 0, 236, 237, 0, 229, 232, - 0, 228, 240, 241, 242, 239, 3, 0, 193, 183, - 184, 186, 216, 185, 219, 218, 245, 246, 180, 197, - 194, 201, 198, 206, 202, 4, 211, 0, 0, 6, - 13, 15, 244, 187, 209, 213, 215, 214, 211, 217, - 190, 188, 0, 0, 0, 0, 0, 0, 0, 5, - 0, 25, 0, 102, 63, 210, 189, 191, 0, 192, - 29, 196, 200, 220, 0, 204, 14, 212, 16, 12, + 2, 183, 1, 209, 234, 225, 232, 236, 231, 227, + 228, 239, 226, 235, 0, 237, 238, 0, 230, 233, + 0, 229, 241, 242, 243, 240, 3, 0, 194, 184, + 185, 187, 217, 186, 220, 219, 246, 247, 181, 198, + 195, 202, 199, 207, 203, 4, 212, 0, 0, 6, + 13, 15, 245, 188, 210, 214, 216, 215, 212, 218, + 191, 189, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 25, 0, 103, 63, 211, 190, 192, 0, 193, + 29, 197, 201, 221, 0, 205, 14, 213, 16, 12, 9, 7, 0, 0, 0, 0, 0, 0, 0, 0, - 243, 167, 166, 162, 163, 164, 165, 168, 169, 172, - 174, 0, 0, 0, 0, 0, 103, 104, 107, 138, - 141, 170, 171, 161, 0, 0, 64, 40, 65, 181, - 31, 33, 0, 222, 207, 0, 0, 0, 0, 11, - 51, 143, 144, 145, 142, 0, 105, 40, 148, 149, - 0, 150, 0, 151, 146, 147, 18, 0, 0, 0, + 244, 168, 167, 163, 164, 165, 166, 169, 170, 173, + 175, 0, 0, 0, 0, 0, 104, 105, 108, 139, + 142, 171, 172, 162, 0, 0, 64, 40, 65, 182, + 31, 33, 0, 223, 208, 0, 0, 0, 0, 11, + 51, 144, 145, 146, 143, 0, 106, 40, 149, 150, + 0, 151, 0, 152, 147, 148, 18, 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, 0, 176, - 0, 159, 160, 173, 175, 0, 17, 0, 211, 102, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 177, + 0, 160, 161, 174, 176, 0, 17, 0, 212, 103, 0, 67, 66, 41, 44, 45, 33, 0, 37, 0, - 34, 35, 15, 221, 223, 0, 71, 8, 27, 0, - 0, 0, 61, 58, 60, 0, 0, 152, 211, 0, - 0, 40, 40, 127, 137, 136, 135, 133, 134, 132, - 131, 130, 129, 128, 0, 125, 124, 123, 122, 121, - 120, 119, 115, 116, 118, 117, 113, 114, 111, 112, - 108, 109, 110, 158, 0, 178, 0, 177, 157, 68, - 69, 42, 0, 48, 0, 102, 63, 0, 39, 30, - 0, 0, 205, 0, 26, 0, 54, 0, 56, 55, - 62, 59, 52, 106, 42, 0, 0, 0, 0, 156, - 155, 0, 43, 49, 50, 0, 0, 32, 36, 38, - 0, 243, 0, 0, 0, 0, 0, 0, 0, 100, - 0, 0, 0, 0, 70, 72, 85, 74, 73, 80, - 0, 0, 0, 101, 0, 28, 53, 57, 0, 139, - 153, 154, 126, 179, 47, 46, 79, 78, 95, 0, - 96, 77, 0, 0, 0, 0, 176, 0, 0, 176, - 75, 81, 86, 0, 84, 19, 21, 0, 0, 76, - 0, 97, 0, 93, 0, 0, 0, 0, 100, 0, - 20, 0, 140, 0, 0, 0, 0, 0, 0, 0, - 82, 0, 0, 24, 0, 87, 98, 94, 91, 99, - 100, 83, 23, 0, 0, 0, 92, 88, 100, 0, - 0, 90 + 34, 35, 15, 222, 224, 0, 71, 8, 27, 0, + 0, 0, 61, 58, 60, 0, 0, 153, 212, 0, + 0, 40, 40, 128, 138, 137, 136, 134, 135, 133, + 132, 131, 130, 129, 0, 126, 125, 124, 123, 122, + 121, 120, 116, 117, 119, 118, 114, 115, 112, 113, + 109, 110, 111, 159, 0, 179, 0, 178, 158, 68, + 69, 42, 0, 48, 0, 103, 63, 0, 39, 30, + 0, 0, 206, 0, 26, 0, 54, 0, 56, 55, + 62, 59, 52, 107, 42, 0, 0, 0, 0, 157, + 156, 0, 43, 49, 50, 0, 0, 32, 36, 38, + 0, 244, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 70, 72, 85, 74, 73, + 80, 0, 0, 0, 102, 0, 28, 53, 57, 0, + 140, 154, 155, 127, 180, 47, 46, 79, 78, 95, + 0, 96, 77, 0, 0, 0, 177, 0, 177, 0, + 0, 177, 75, 81, 86, 0, 84, 19, 21, 0, + 0, 76, 0, 97, 0, 0, 93, 0, 0, 0, + 0, 101, 0, 20, 0, 141, 0, 0, 0, 0, + 0, 0, 0, 0, 82, 0, 0, 24, 0, 87, + 99, 98, 94, 91, 100, 101, 83, 23, 0, 0, + 0, 92, 88, 101, 0, 0, 90 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 1, 26, 71, 136, 48, 72, 208, 50, 325, - 367, 379, 91, 219, 78, 131, 206, 209, 210, 211, + -1, 1, 26, 71, 136, 48, 72, 208, 50, 326, + 369, 382, 91, 219, 78, 131, 206, 209, 210, 211, 202, 203, 204, 205, 222, 223, 224, 225, 125, 126, - 217, 283, 326, 327, 328, 389, 329, 330, 331, 332, - 115, 116, 333, 146, 118, 119, 120, 121, 122, 266, + 217, 283, 327, 328, 329, 393, 330, 331, 332, 333, + 115, 116, 334, 146, 118, 119, 120, 121, 122, 266, 267, 39, 62, 27, 79, 127, 29, 30, 63, 64, 66, 135, 65, 53, 67, 54, 31, 32, 84, 33, 34, 35, 123, 51, 52 @@ -871,184 +874,184 @@ static const yytype_int16 yydefgoto[] = /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -366 +#define YYPACT_NINF -331 static const yytype_int16 yypact[] = { - -366, 541, -366, -366, -366, -366, -366, -366, -366, -366, - -366, -366, -366, -366, -3, -366, -366, -3, -366, -366, - 140, -366, -366, -366, -366, -366, -366, 161, -366, -366, - 949, 914, -366, 949, -366, -366, -366, -366, -366, -366, - -64, -366, -60, -366, -17, -366, -366, 21, 70, 316, - 51, -366, -366, 949, -366, -366, -366, -366, -366, -366, - 949, 949, 914, -13, -13, 57, 9, 154, 54, -366, - 21, -366, 164, 745, 836, -366, -41, 949, 875, -366, - -366, -366, -366, 167, 7, -366, -366, -366, -366, -366, - 181, 914, 677, 745, 745, 745, 745, 607, 745, 745, - -366, -366, -366, -366, -366, -366, -366, -366, -366, -366, - -366, 779, 813, 745, 745, 95, -366, 1067, -366, -366, - 422, 141, 145, -366, 176, 139, 225, 128, -366, -366, - -366, 289, 745, 57, -366, 57, 149, 21, 169, -366, - 1067, -366, -366, -366, -366, 31, 1067, 130, -366, -366, - 607, -366, 607, -366, -366, -366, -366, 745, 745, 745, - 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, - 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, - 745, 745, 745, 745, 745, 745, 745, 53, 745, 745, - 53, -366, -366, -366, -366, 199, -366, 836, -366, 745, - 147, -366, -366, -366, 111, -366, 289, 745, -366, 243, - 247, -366, 235, 1067, -366, 13, -366, -366, -366, 222, - 53, 745, 260, 255, 169, 172, 745, -366, -366, -6, - 180, 130, 130, 1067, 1067, 1067, 1067, 1067, 1067, 1067, - 1067, 1067, 1067, 1067, 16, 1084, 1100, 1115, 1129, 1142, - 1153, 1153, 319, 319, 319, 319, 303, 303, 295, 295, - -366, -366, -366, -366, 11, 1067, 186, 269, -366, -366, - -366, 190, 188, -366, 187, 745, 836, 280, -366, -366, - 289, 745, -366, 338, -366, 21, -366, 191, -366, -366, - 284, 255, -366, -366, 217, 711, 202, 204, 745, -366, - -366, 745, -366, -366, -366, 198, 207, -366, -366, -366, - 309, 296, 312, 745, 320, 307, 435, 53, 302, 745, - 305, 306, 308, 318, -366, -366, 504, -366, -366, -366, - 149, 292, 342, 354, 259, -366, -366, -366, 169, -366, - -366, -366, 421, -366, -366, -366, -366, -366, -366, 1036, - -366, -366, 277, 358, 745, 359, 745, 745, 745, 745, - -366, -366, -366, 325, -366, -366, 361, 234, 272, -366, - 326, -366, 64, -366, 276, 66, 67, 281, 607, 367, - -366, 21, -366, 745, 435, 371, 435, 435, 372, 397, - -366, 21, 677, -366, 68, 368, -366, -366, -366, -366, - 745, 427, -366, 461, 435, 462, -366, -366, 745, 377, - 435, -366 + -331, 548, -331, -331, -331, -331, -331, -331, -331, -331, + -331, -331, -331, -331, -3, -331, -331, -3, -331, -331, + 149, -331, -331, -331, -331, -331, -331, 264, -331, -331, + 965, 929, -331, 965, -331, -331, -331, -331, -331, -331, + -75, -331, -72, -331, -60, -331, -331, 307, 60, 270, + 156, -331, -331, 965, -331, -331, -331, -331, -331, -331, + 965, 965, 929, -44, -44, 29, -15, 199, -10, -331, + 307, -331, 83, 756, 849, -331, 140, 965, 889, -331, + -331, -331, -331, 86, 12, -331, -331, -331, -331, -331, + 90, 929, 686, 756, 756, 756, 756, 615, 756, 756, + -331, -331, -331, -331, -331, -331, -331, -331, -331, -331, + -331, 791, 826, 756, 756, 9, -331, 1084, -331, -331, + 708, 54, 57, -331, 110, 56, 152, 310, -331, -331, + -331, 279, 756, 29, -331, 29, 63, 307, 165, -331, + 1084, -331, -331, -331, -331, 30, 1084, 44, -331, -331, + 615, -331, 615, -331, -331, -331, -331, 756, 756, 756, + 756, 756, 756, 756, 756, 756, 756, 756, 756, 756, + 756, 756, 756, 756, 756, 756, 756, 756, 756, 756, + 756, 756, 756, 756, 756, 756, 756, 157, 756, 756, + 157, -331, -331, -331, -331, 115, -331, 849, -331, 756, + 128, -331, -331, -331, 182, -331, 279, 756, -331, 164, + 200, -331, 208, 1084, -331, 13, -331, -331, -331, 262, + 157, 756, 225, 228, 165, 73, 756, -331, -331, -7, + 150, 44, 44, 1084, 1084, 1084, 1084, 1084, 1084, 1084, + 1084, 1084, 1084, 1084, 28, 304, 1100, 1115, 1129, 1142, + 1153, 1153, 433, 433, 433, 433, 333, 333, 265, 265, + -331, -331, -331, -331, 8, 1084, 153, 236, -331, -331, + -331, 147, 158, -331, 161, 756, 849, 247, -331, -331, + 279, 756, -331, 341, -331, 307, -331, 175, -331, -331, + 254, 228, -331, -331, 135, 721, 188, 190, 756, -331, + -331, 756, -331, -331, -331, 191, 211, -331, -331, -331, + 298, 301, 338, 756, 343, 339, 439, 157, 319, 321, + 756, 322, 323, 324, 332, -331, -331, 509, -331, -331, + -331, 63, 306, 372, 373, 277, -331, -331, -331, 165, + -331, -331, -331, 425, -331, -331, -331, -331, -331, -331, + 1053, -331, -331, 293, 375, 756, 756, 400, 756, 756, + 756, 756, -331, -331, -331, 396, -331, -331, 430, 285, + 377, -331, 431, -331, 55, 381, -331, 382, 62, 64, + 383, 615, 473, -331, 307, -331, 756, 439, 479, 490, + 439, 439, 493, 497, -331, 307, 686, -331, 66, 440, + -331, -331, -331, -331, -331, 756, 499, -331, 498, 439, + 504, -331, -331, 756, 415, 439, -331 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -366, -366, -366, -366, -366, 400, -366, -26, -366, -366, - -365, -366, -366, 189, -366, -366, -366, 266, 209, -366, - -122, -187, -366, -366, -82, 254, -366, 133, 216, 299, - 168, -366, -366, 171, -304, -366, 173, -366, -366, -227, - -181, -183, -83, -45, -38, 137, -366, -366, -366, -308, - 203, 2, -366, -366, -1, 0, -88, 472, -366, -366, - -366, -366, -366, -10, -51, 208, -366, 474, 22, 256, - 265, -24, -52, -127, -12 + -331, -331, -331, -331, -331, 445, -331, -26, -331, -331, + -330, -331, -331, 233, -331, -331, -331, 313, 230, -331, + -132, -187, -331, -331, -82, 292, -331, 181, 245, 326, + 193, -331, -331, 198, -227, -331, 203, -331, -331, -309, + -181, -183, -83, -45, -38, 243, -331, -331, -331, -175, + 226, 10, -331, -331, -1, 0, -88, 495, -331, -331, + -331, -331, -331, -14, -51, -28, -331, 501, -85, 218, + 231, -24, -52, -127, -12 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -204 +#define YYTABLE_NINF -205 static const yytype_int16 yytable[] = { 28, 49, 40, 137, 212, 42, 57, 76, 44, 57, - 139, 133, 352, 274, 145, 226, 393, 133, 272, 41, - 226, 68, 128, 61, 278, 230, 401, 228, 117, 57, - -195, 199, 229, 298, -199, 226, 57, 57, 287, 36, - 37, 86, 274, 87, 90, 22, 23, 140, 374, 24, - 77, 377, 87, 57, 46, 141, 142, 143, 144, 47, - 148, 149, 80, 36, 37, 81, 82, 145, 226, 145, - 226, 226, 226, 69, 70, 154, 155, -203, 130, 212, - 395, 38, 397, 398, 302, 244, 273, 213, 73, 74, - 28, 38, 355, 140, 305, 36, 37, 147, 309, 83, - 407, 201, 134, 85, 299, 264, 411, 302, 282, 296, - 297, 218, 233, 234, 235, 236, 237, 238, 239, 240, + 139, 357, 226, 274, 145, 230, 133, 133, 272, 61, + -196, 68, 128, -200, 278, 75, 228, 41, 117, 57, + 199, 229, 226, 75, 226, -204, 57, 57, 287, 36, + 37, 86, 274, 87, 90, 298, 77, 140, 214, 75, + 215, 38, 87, 57, 397, 141, 142, 143, 144, 226, + 148, 149, 80, 69, 70, 406, 226, 145, 226, 145, + 226, 83, 394, 81, 82, 154, 155, 228, 130, 212, + 85, 199, 229, 88, 302, 244, 273, 213, 92, 353, + 28, 132, 38, 140, 305, -10, 410, 147, 309, 296, + 297, 201, 299, 156, 414, 264, 193, 302, 134, 282, + 194, 218, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 227, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, - 261, 262, 290, 293, 265, 128, 88, 271, 275, 276, - 231, 390, 232, 212, 117, 214, 384, 215, 386, 387, - 403, 198, 117, 228, 45, 199, 200, 199, 229, 92, - 36, 37, 132, 405, 68, 263, 117, 294, 268, 140, - 198, 409, 36, 37, 199, 200, -10, 46, 156, 36, - 37, 93, 47, 193, 46, 334, 36, 37, 194, 47, - 94, 95, 96, 36, 37, 220, 221, 97, 286, 98, - 99, 100, 195, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 198, 128, 284, 285, 199, 200, 197, - 117, 196, 36, 37, 43, 269, 117, 380, 381, 273, - 22, 23, 111, 216, 24, 86, 279, 87, 151, 153, - 228, 280, 281, 342, 199, 229, 265, 339, 112, 218, - 289, 75, 365, 138, 288, 113, 114, 292, 349, 75, - 87, 372, 295, 301, 375, 376, 22, 23, 300, 304, - 24, 303, 28, 307, 336, 75, 55, 60, 337, 55, - 391, 344, 46, 140, 340, 56, 341, 47, 56, 345, - 394, 36, 37, 22, 23, 353, 207, 24, 366, 55, - 402, 265, 346, 347, 265, 348, 55, 55, 56, -9, - -9, -10, 46, 350, 351, 56, 56, 47, 184, 185, - 186, 36, 37, 55, 182, 183, 184, 185, 186, 310, - 354, -100, 56, 356, 357, 364, 358, 140, 180, 181, - 182, 183, 184, 185, 186, 366, 359, 363, 226, 370, - 93, 371, 373, 378, 383, 366, -22, 382, 385, 94, - 95, 96, 392, 388, 396, 399, 97, 28, 98, 99, - 311, 3, 101, 102, 103, 104, 105, 106, 107, 108, - 109, 110, 4, 312, 313, 5, 314, 315, 316, 6, - 400, 7, 8, -89, 317, 318, 9, 10, 11, 319, - 12, 111, 320, 13, 14, 321, 15, 16, 17, 18, - 322, 19, 20, 21, 22, 23, 323, 112, 24, 25, - 404, 381, -85, 324, 113, 114, 310, 168, -100, 169, - 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, - 180, 181, 182, 183, 184, 185, 186, 93, 187, 188, - 189, 190, 191, 192, 406, 408, 94, 95, 96, 410, - 89, 368, 277, 97, 335, 98, 99, 311, 291, 101, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 308, - 312, 313, 306, 314, 315, 316, 270, 360, 362, 361, - -89, 317, 318, 58, 343, 59, 319, -100, 111, 320, - 0, 0, 321, 0, 0, 0, 0, 322, 0, 0, - 0, 0, 0, 323, 112, 0, 93, 0, 0, -85, - 0, 113, 114, 0, 0, 94, 95, 96, 0, 0, - 0, 2, 97, 0, 98, 99, 311, 0, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 110, 0, 312, - 313, 0, 314, 315, 316, 0, 0, 0, 0, -89, - 317, 318, 0, 0, 0, 319, 0, 111, 320, 0, - 0, 321, 0, 0, 3, 0, 322, 0, 0, 0, - 0, 0, 323, 112, 0, 4, 0, 0, 5, 0, - 113, 114, 6, 0, 7, 8, 0, 0, 0, 9, - 10, 11, 0, 12, 0, 0, 13, 14, 0, 15, - 16, 17, 18, 0, 19, 20, 21, 22, 23, 93, - 0, 24, 25, 0, 0, 0, 0, 0, 94, 95, - 96, 0, 0, 0, 0, 97, 0, 98, 99, 100, - 3, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 4, 0, 0, 5, 0, 0, 0, 6, 0, - 7, 8, 0, 0, 0, 9, 10, 11, 0, 12, - 111, 0, 13, 14, 0, 15, 16, 17, 18, 0, - 19, 20, 21, 22, 23, 0, 112, 24, 25, 93, - 0, 0, 0, 113, 114, 0, 0, 0, 94, 95, - 96, 0, 0, 0, 0, 97, 0, 98, 99, 100, - 0, 101, 102, 103, 104, 105, 106, 107, 108, 109, - 110, 0, 0, 93, 0, 0, 0, 0, 0, 0, + 261, 262, 290, 293, 265, 128, 195, 271, 387, 196, + 231, 269, 232, 212, 117, 390, 197, 391, 216, 408, + 399, 198, 117, 402, 403, 199, 200, 279, 228, 292, + 36, 37, 199, 229, 68, 263, 117, 294, 268, 140, + 198, 375, 412, 377, 199, 200, 380, 93, 416, 36, + 37, 36, 37, 73, 74, 335, 94, 95, 96, 36, + 37, 220, 221, 97, 280, 98, 99, 100, 286, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 275, + 276, 273, 22, 23, 128, 281, 24, 22, 23, 288, + 117, 24, 46, 289, 22, 23, 117, 47, 24, 111, + 301, 36, 37, 295, 43, 86, 300, 87, 55, 60, + 307, 55, 303, 343, 304, 112, 265, 340, 338, 218, + 138, 56, 113, 114, 56, 284, 285, 45, 350, 337, + 87, 55, 374, -9, -9, -10, 378, 379, 55, 55, + 367, 341, 28, 342, 56, 345, 22, 23, 383, 384, + 24, 56, 56, 395, 140, 55, 207, 46, 184, 185, + 186, 347, 47, 398, 346, 354, 36, 37, 56, 368, + 46, 265, 46, 265, 407, 47, 265, 47, 348, 36, + 37, 36, 37, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 46, 349, 310, 198, -101, 47, 351, 199, 200, 36, + 37, 140, 36, 37, 151, 153, 352, 355, 368, 356, + 358, 359, 360, 93, 182, 183, 184, 185, 186, 368, + 361, 365, 94, 95, 96, 366, 372, 226, 373, 97, + 28, 98, 99, 311, 3, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 4, 312, 313, 5, 314, + 315, 316, 6, 376, 7, 8, -89, 317, 318, 9, + 10, 319, 11, 320, 12, 111, 321, 13, 14, 322, + 15, 16, 17, 18, 323, 19, 20, 21, 22, 23, + 324, 112, 24, 25, 381, -22, -85, 325, 113, 114, + 310, 168, -101, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 93, 180, 181, 182, 183, 184, 185, 186, 386, + 94, 95, 96, 385, 388, 389, 392, 97, 396, 98, + 99, 311, 400, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 401, 312, 313, 404, 314, 315, 316, + 405, 411, 409, 384, -89, 317, 318, 413, 415, 319, + 308, 320, -101, 111, 321, 89, 291, 322, 336, 277, + 370, 306, 323, 270, 364, 362, 58, 344, 324, 112, + 363, 93, 59, 0, -85, 0, 113, 114, 0, 0, + 94, 95, 96, 0, 0, 0, 0, 97, 2, 98, + 99, 311, 0, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 0, 312, 313, 0, 314, 315, 316, + 0, 0, 0, 0, -89, 317, 318, 0, 0, 319, + 0, 320, 0, 111, 321, 0, 0, 322, 0, 0, + 0, 3, 323, 0, 0, 0, 0, 0, 324, 112, + 0, 0, 4, 0, 0, 5, 113, 114, 0, 6, + 0, 7, 8, 0, 0, 0, 9, 10, 0, 11, + 0, 12, 0, 0, 13, 14, 0, 15, 16, 17, + 18, 0, 19, 20, 21, 22, 23, 93, 0, 24, + 25, 0, 0, 0, 0, 0, 94, 95, 96, 0, + 0, 0, 0, 97, 0, 98, 99, 100, 3, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 4, + 0, 0, 5, 0, 0, 0, 6, 0, 7, 8, + 0, 0, 0, 9, 10, 0, 11, 0, 12, 111, + 0, 13, 14, 0, 15, 16, 17, 18, 0, 19, + 20, 21, 22, 23, 0, 112, 24, 25, 93, 0, + 0, 0, 113, 114, 0, 0, 0, 94, 95, 96, + 0, 0, 0, 0, 97, 0, 98, 99, 100, 0, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 0, 0, 93, 187, 188, 189, 190, 191, 192, 0, 0, 94, 95, 96, 0, 0, 0, 0, 97, 111, 98, 99, 100, 0, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 0, 112, 93, 0, 0, - 0, 138, 0, 113, 114, 0, 94, 95, 96, 0, - 0, 0, 0, 97, 111, 98, 99, 100, 0, 101, - 102, 103, 104, 105, 106, 107, 108, 109, 110, 0, - 112, 93, 0, 0, 0, 338, 0, 113, 114, 0, - 94, 95, 96, 0, 0, 0, 0, 150, 111, 98, - 99, 100, 0, 101, 102, 103, 104, 105, 106, 107, - 108, 109, 110, 0, 112, 93, 0, 0, 0, 0, - 0, 113, 114, 0, 94, 95, 96, 0, 0, 0, - 0, 152, 111, 98, 99, 100, 0, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 0, 112, 0, - 0, 0, 124, 0, 0, 113, 114, 0, 100, 3, - 0, 0, 0, 0, 0, 0, 111, 0, 0, 0, - 4, 0, 0, 5, 0, 0, 0, 6, 0, 7, - 8, 0, 112, 0, 9, 10, 11, 0, 12, 113, - 114, 13, 14, 0, 15, 16, 17, 18, 3, 19, - 20, 21, 22, 23, 0, 0, 24, 25, 0, 4, - 0, 0, 5, 0, 0, 0, 6, 0, 7, 8, - 0, 0, 0, 9, 10, 11, 0, 12, 0, 0, - 13, 14, 0, 15, 16, 17, 18, 3, 19, 20, - 21, 22, 23, 0, 0, 24, 25, 0, 4, 0, - 129, 5, 0, 0, 0, 6, 0, 7, 8, 0, - 0, 0, 9, 10, 11, 0, 12, 0, 0, 13, - 14, 0, 15, 16, 17, 18, 0, 19, 20, 21, - 22, 23, 0, 4, 24, 25, 5, 0, 0, 0, - 6, 0, 7, 8, 0, 0, 0, 9, 10, 11, - 0, 12, 0, 0, 13, 0, 0, 15, 16, 0, - 18, 0, 19, 0, 21, 22, 23, 0, 0, 24, - 25, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 369, 169, 170, 171, 172, 173, 174, - 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 168, 0, 169, 170, 171, 172, 173, - 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, - 184, 185, 186, 170, 171, 172, 173, 174, 175, 176, + 106, 107, 108, 109, 110, 0, 112, 0, 93, 0, + 0, 138, 0, 113, 114, 0, 0, 94, 95, 96, + 0, 0, 0, 0, 97, 111, 98, 99, 100, 0, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 112, 0, 93, 0, 0, 339, 0, 113, 114, + 0, 0, 94, 95, 96, 0, 0, 0, 0, 150, + 111, 98, 99, 100, 0, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 0, 112, 0, 93, 0, + 0, 0, 0, 113, 114, 0, 0, 94, 95, 96, + 0, 0, 0, 0, 152, 111, 98, 99, 100, 0, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 0, 112, 0, 0, 0, 124, 0, 0, 113, 114, + 0, 100, 3, 0, 0, 0, 0, 0, 0, 0, + 111, 0, 0, 4, 0, 0, 5, 0, 0, 0, + 6, 0, 7, 8, 0, 0, 112, 9, 10, 0, + 11, 0, 12, 113, 114, 13, 14, 0, 15, 16, + 17, 18, 3, 19, 20, 21, 22, 23, 0, 0, + 24, 25, 0, 4, 0, 0, 5, 0, 0, 0, + 6, 0, 7, 8, 0, 0, 0, 9, 10, 0, + 11, 0, 12, 0, 0, 13, 14, 0, 15, 16, + 17, 18, 3, 19, 20, 21, 22, 23, 0, 0, + 24, 25, 0, 4, 0, 129, 5, 0, 0, 0, + 6, 0, 7, 8, 0, 0, 0, 9, 10, 0, + 11, 0, 12, 0, 0, 13, 14, 0, 15, 16, + 17, 18, 0, 19, 20, 21, 22, 23, 0, 4, + 24, 25, 5, 0, 0, 0, 6, 0, 7, 8, + 0, 0, 0, 9, 10, 0, 11, 0, 12, 0, + 0, 13, 0, 0, 15, 16, 0, 18, 0, 19, + 0, 21, 22, 23, 0, 0, 24, 25, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 371, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 0, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 172, 173, 174, 175, @@ -1062,116 +1065,116 @@ static const yytype_int16 yytable[] = static const yytype_int16 yycheck[] = { 1, 27, 14, 91, 131, 17, 30, 58, 20, 33, - 92, 4, 316, 200, 97, 4, 381, 4, 199, 17, - 4, 47, 74, 33, 207, 147, 391, 33, 73, 53, - 94, 37, 38, 17, 94, 4, 60, 61, 221, 42, - 43, 67, 229, 67, 70, 86, 87, 92, 356, 90, - 60, 359, 76, 77, 33, 93, 94, 95, 96, 38, - 98, 99, 62, 42, 43, 63, 64, 150, 4, 152, - 4, 4, 4, 3, 4, 113, 114, 94, 78, 206, - 384, 94, 386, 387, 271, 168, 92, 132, 37, 38, - 91, 94, 319, 138, 275, 42, 43, 97, 281, 42, - 404, 127, 95, 94, 93, 188, 410, 294, 95, 231, - 232, 137, 157, 158, 159, 160, 161, 162, 163, 164, - 165, 166, 167, 92, 169, 170, 171, 172, 173, 174, + 92, 320, 4, 200, 97, 147, 4, 4, 199, 33, + 95, 47, 74, 95, 207, 53, 33, 17, 73, 53, + 37, 38, 4, 61, 4, 95, 60, 61, 221, 42, + 43, 67, 229, 67, 70, 17, 60, 92, 133, 77, + 135, 95, 76, 77, 384, 93, 94, 95, 96, 4, + 98, 99, 62, 3, 4, 395, 4, 150, 4, 152, + 4, 42, 381, 63, 64, 113, 114, 33, 78, 206, + 95, 37, 38, 93, 271, 168, 93, 132, 5, 316, + 91, 5, 95, 138, 275, 5, 405, 97, 281, 231, + 232, 127, 94, 94, 413, 188, 52, 294, 96, 96, + 53, 137, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 93, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 224, 226, 189, 197, 92, 198, 37, 38, - 150, 378, 152, 280, 199, 133, 92, 135, 92, 92, - 92, 33, 207, 33, 3, 37, 38, 37, 38, 5, - 42, 43, 5, 400, 200, 187, 221, 228, 190, 224, - 33, 408, 42, 43, 37, 38, 5, 33, 93, 42, - 43, 22, 38, 52, 33, 283, 42, 43, 53, 38, - 31, 32, 33, 42, 43, 36, 37, 38, 220, 40, - 41, 42, 36, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 33, 276, 3, 4, 37, 38, 4, - 275, 92, 42, 43, 94, 36, 281, 3, 4, 92, - 86, 87, 73, 94, 90, 271, 3, 271, 111, 112, - 33, 4, 17, 298, 37, 38, 301, 295, 89, 285, - 5, 53, 3, 94, 4, 96, 97, 95, 313, 61, - 294, 354, 92, 4, 357, 358, 86, 87, 92, 92, - 90, 93, 283, 3, 93, 77, 30, 31, 4, 33, - 378, 93, 33, 338, 92, 30, 92, 38, 33, 92, - 383, 42, 43, 86, 87, 317, 17, 90, 334, 53, - 392, 356, 3, 17, 359, 3, 60, 61, 53, 3, - 4, 5, 33, 3, 17, 60, 61, 38, 33, 34, - 35, 42, 43, 77, 31, 32, 33, 34, 35, 1, - 38, 3, 77, 38, 38, 3, 38, 392, 29, 30, - 31, 32, 33, 34, 35, 381, 38, 65, 4, 82, - 22, 3, 3, 38, 38, 391, 5, 95, 92, 31, - 32, 33, 5, 92, 3, 3, 38, 378, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, - 3, 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, - 62, 4, 94, 95, 96, 97, 1, 16, 3, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 22, 36, 37, - 38, 39, 40, 41, 3, 3, 31, 32, 33, 92, - 70, 338, 206, 38, 285, 40, 41, 42, 224, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 280, - 55, 56, 276, 58, 59, 60, 197, 326, 330, 326, - 65, 66, 67, 31, 301, 31, 71, 3, 73, 74, - -1, -1, 77, -1, -1, -1, -1, 82, -1, -1, - -1, -1, -1, 88, 89, -1, 22, -1, -1, 94, - -1, 96, 97, -1, -1, 31, 32, 33, -1, -1, - -1, 0, 38, -1, 40, 41, 42, -1, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, -1, 55, - 56, -1, 58, 59, 60, -1, -1, -1, -1, 65, - 66, 67, -1, -1, -1, 71, -1, 73, 74, -1, - -1, 77, -1, -1, 43, -1, 82, -1, -1, -1, - -1, -1, 88, 89, -1, 54, -1, -1, 57, -1, - 96, 97, 61, -1, 63, 64, -1, -1, -1, 68, - 69, 70, -1, 72, -1, -1, 75, 76, -1, 78, - 79, 80, 81, -1, 83, 84, 85, 86, 87, 22, - -1, 90, 91, -1, -1, -1, -1, -1, 31, 32, - 33, -1, -1, -1, -1, 38, -1, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, -1, -1, 57, -1, -1, -1, 61, -1, - 63, 64, -1, -1, -1, 68, 69, 70, -1, 72, - 73, -1, 75, 76, -1, 78, 79, 80, 81, -1, - 83, 84, 85, 86, 87, -1, 89, 90, 91, 22, - -1, -1, -1, 96, 97, -1, -1, -1, 31, 32, - 33, -1, -1, -1, -1, 38, -1, 40, 41, 42, - -1, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, -1, -1, 22, -1, -1, -1, -1, -1, -1, - -1, -1, 31, 32, 33, -1, -1, -1, -1, 38, - 73, 40, 41, 42, -1, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, -1, 89, 22, -1, -1, - -1, 94, -1, 96, 97, -1, 31, 32, 33, -1, - -1, -1, -1, 38, 73, 40, 41, 42, -1, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, - 89, 22, -1, -1, -1, 94, -1, 96, 97, -1, - 31, 32, 33, -1, -1, -1, -1, 38, 73, 40, + 185, 186, 224, 226, 189, 197, 36, 198, 93, 93, + 150, 36, 152, 280, 199, 93, 4, 93, 95, 93, + 387, 33, 207, 390, 391, 37, 38, 3, 33, 96, + 42, 43, 37, 38, 200, 187, 221, 228, 190, 224, + 33, 356, 409, 358, 37, 38, 361, 22, 415, 42, + 43, 42, 43, 37, 38, 283, 31, 32, 33, 42, + 43, 36, 37, 38, 4, 40, 41, 42, 220, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 37, + 38, 93, 87, 88, 276, 17, 91, 87, 88, 4, + 275, 91, 33, 5, 87, 88, 281, 38, 91, 74, + 4, 42, 43, 93, 95, 271, 93, 271, 30, 31, + 3, 33, 94, 298, 93, 90, 301, 295, 4, 285, + 95, 30, 97, 98, 33, 3, 4, 3, 313, 94, + 294, 53, 355, 3, 4, 5, 359, 360, 60, 61, + 3, 93, 283, 93, 53, 94, 87, 88, 3, 4, + 91, 60, 61, 381, 339, 77, 17, 33, 33, 34, + 35, 3, 38, 386, 93, 317, 42, 43, 77, 335, + 33, 356, 33, 358, 396, 38, 361, 38, 17, 42, + 43, 42, 43, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 33, 3, 1, 33, 3, 38, 3, 37, 38, 42, + 43, 396, 42, 43, 111, 112, 17, 38, 384, 38, + 38, 38, 38, 22, 31, 32, 33, 34, 35, 395, + 38, 65, 31, 32, 33, 3, 83, 4, 3, 38, + 381, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 3, 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, 38, 5, 95, 96, 97, 98, + 1, 16, 3, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 22, 29, 30, 31, 32, 33, 34, 35, 38, + 31, 32, 33, 96, 93, 93, 93, 38, 5, 40, + 41, 42, 3, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 3, 55, 56, 3, 58, 59, 60, + 3, 3, 62, 4, 65, 66, 67, 3, 93, 70, + 280, 72, 3, 74, 75, 70, 224, 78, 285, 206, + 339, 276, 83, 197, 331, 327, 31, 301, 89, 90, + 327, 22, 31, -1, 95, -1, 97, 98, -1, -1, + 31, 32, 33, -1, -1, -1, -1, 38, 0, 40, 41, 42, -1, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, -1, 89, 22, -1, -1, -1, -1, - -1, 96, 97, -1, 31, 32, 33, -1, -1, -1, - -1, 38, 73, 40, 41, 42, -1, 44, 45, 46, - 47, 48, 49, 50, 51, 52, 53, -1, 89, -1, - -1, -1, 36, -1, -1, 96, 97, -1, 42, 43, - -1, -1, -1, -1, -1, -1, 73, -1, -1, -1, - 54, -1, -1, 57, -1, -1, -1, 61, -1, 63, - 64, -1, 89, -1, 68, 69, 70, -1, 72, 96, - 97, 75, 76, -1, 78, 79, 80, 81, 43, 83, - 84, 85, 86, 87, -1, -1, 90, 91, -1, 54, + 51, 52, 53, -1, 55, 56, -1, 58, 59, 60, + -1, -1, -1, -1, 65, 66, 67, -1, -1, 70, + -1, 72, -1, 74, 75, -1, -1, 78, -1, -1, + -1, 43, 83, -1, -1, -1, -1, -1, 89, 90, + -1, -1, 54, -1, -1, 57, 97, 98, -1, 61, + -1, 63, 64, -1, -1, -1, 68, 69, -1, 71, + -1, 73, -1, -1, 76, 77, -1, 79, 80, 81, + 82, -1, 84, 85, 86, 87, 88, 22, -1, 91, + 92, -1, -1, -1, -1, -1, 31, 32, 33, -1, + -1, -1, -1, 38, -1, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, -1, -1, 57, -1, -1, -1, 61, -1, 63, 64, - -1, -1, -1, 68, 69, 70, -1, 72, -1, -1, - 75, 76, -1, 78, 79, 80, 81, 43, 83, 84, - 85, 86, 87, -1, -1, 90, 91, -1, 54, -1, - 95, 57, -1, -1, -1, 61, -1, 63, 64, -1, - -1, -1, 68, 69, 70, -1, 72, -1, -1, 75, - 76, -1, 78, 79, 80, 81, -1, 83, 84, 85, - 86, 87, -1, 54, 90, 91, 57, -1, -1, -1, - 61, -1, 63, 64, -1, -1, -1, 68, 69, 70, - -1, 72, -1, -1, 75, -1, -1, 78, 79, -1, - 81, -1, 83, -1, 85, 86, 87, -1, -1, 90, - 91, 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, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, -1, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, - 33, 34, 35, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, 68, 69, -1, 71, -1, 73, 74, + -1, 76, 77, -1, 79, 80, 81, 82, -1, 84, + 85, 86, 87, 88, -1, 90, 91, 92, 22, -1, + -1, -1, 97, 98, -1, -1, -1, 31, 32, 33, + -1, -1, -1, -1, 38, -1, 40, 41, 42, -1, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + -1, -1, -1, 22, 36, 37, 38, 39, 40, 41, + -1, -1, 31, 32, 33, -1, -1, -1, -1, 38, + 74, 40, 41, 42, -1, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, -1, 90, -1, 22, -1, + -1, 95, -1, 97, 98, -1, -1, 31, 32, 33, + -1, -1, -1, -1, 38, 74, 40, 41, 42, -1, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + -1, 90, -1, 22, -1, -1, 95, -1, 97, 98, + -1, -1, 31, 32, 33, -1, -1, -1, -1, 38, + 74, 40, 41, 42, -1, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, -1, 90, -1, 22, -1, + -1, -1, -1, 97, 98, -1, -1, 31, 32, 33, + -1, -1, -1, -1, 38, 74, 40, 41, 42, -1, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + -1, 90, -1, -1, -1, 36, -1, -1, 97, 98, + -1, 42, 43, -1, -1, -1, -1, -1, -1, -1, + 74, -1, -1, 54, -1, -1, 57, -1, -1, -1, + 61, -1, 63, 64, -1, -1, 90, 68, 69, -1, + 71, -1, 73, 97, 98, 76, 77, -1, 79, 80, + 81, 82, 43, 84, 85, 86, 87, 88, -1, -1, + 91, 92, -1, 54, -1, -1, 57, -1, -1, -1, + 61, -1, 63, 64, -1, -1, -1, 68, 69, -1, + 71, -1, 73, -1, -1, 76, 77, -1, 79, 80, + 81, 82, 43, 84, 85, 86, 87, 88, -1, -1, + 91, 92, -1, 54, -1, 96, 57, -1, -1, -1, + 61, -1, 63, 64, -1, -1, -1, 68, 69, -1, + 71, -1, 73, -1, -1, 76, 77, -1, 79, 80, + 81, 82, -1, 84, 85, 86, 87, 88, -1, 54, + 91, 92, 57, -1, -1, -1, 61, -1, 63, 64, + -1, -1, -1, 68, 69, -1, 71, -1, 73, -1, + -1, 76, -1, -1, 79, 80, -1, 82, -1, 84, + -1, 86, 87, 88, -1, -1, 91, 92, 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, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, -1, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 21, 22, 23, 24, @@ -1186,48 +1189,48 @@ static const yytype_int16 yycheck[] = symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { - 0, 99, 0, 43, 54, 57, 61, 63, 64, 68, - 69, 70, 72, 75, 76, 78, 79, 80, 81, 83, - 84, 85, 86, 87, 90, 91, 100, 151, 152, 154, - 155, 164, 165, 167, 168, 169, 42, 43, 94, 149, - 172, 149, 172, 94, 172, 3, 33, 38, 103, 105, - 106, 171, 172, 161, 163, 167, 168, 169, 155, 165, - 167, 161, 150, 156, 157, 160, 158, 162, 105, 3, - 4, 101, 104, 37, 38, 163, 162, 161, 112, 152, - 153, 149, 149, 42, 166, 94, 105, 169, 92, 103, - 105, 110, 5, 22, 31, 32, 33, 38, 40, 41, + 0, 100, 0, 43, 54, 57, 61, 63, 64, 68, + 69, 71, 73, 76, 77, 79, 80, 81, 82, 84, + 85, 86, 87, 88, 91, 92, 101, 152, 153, 155, + 156, 165, 166, 168, 169, 170, 42, 43, 95, 150, + 173, 150, 173, 95, 173, 3, 33, 38, 104, 106, + 107, 172, 173, 162, 164, 168, 169, 170, 156, 166, + 168, 162, 151, 157, 158, 161, 159, 163, 106, 3, + 4, 102, 105, 37, 38, 164, 163, 162, 113, 153, + 154, 150, 150, 42, 167, 95, 106, 170, 93, 104, + 106, 111, 5, 22, 31, 32, 33, 38, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 73, 89, 96, 97, 138, 139, 141, 142, 143, - 144, 145, 146, 170, 36, 126, 127, 153, 170, 95, - 153, 113, 5, 4, 95, 159, 102, 154, 94, 122, - 141, 142, 142, 142, 142, 140, 141, 153, 142, 142, - 38, 143, 38, 143, 142, 142, 93, 5, 6, 7, + 53, 74, 90, 97, 98, 139, 140, 142, 143, 144, + 145, 146, 147, 171, 36, 127, 128, 154, 171, 96, + 154, 114, 5, 4, 96, 160, 103, 155, 95, 123, + 142, 143, 143, 143, 143, 141, 142, 154, 143, 143, + 38, 144, 38, 144, 143, 143, 94, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 52, 53, 36, 92, 4, 33, 37, - 38, 105, 118, 119, 120, 121, 114, 17, 105, 115, - 116, 117, 171, 141, 166, 166, 94, 128, 105, 111, - 36, 37, 122, 123, 124, 125, 4, 92, 33, 38, - 118, 153, 153, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 140, 141, 141, 141, 141, 141, - 141, 141, 141, 141, 141, 141, 141, 141, 141, 141, - 141, 141, 141, 172, 140, 141, 147, 148, 172, 36, - 127, 162, 138, 92, 119, 37, 38, 115, 139, 3, - 4, 17, 95, 129, 3, 4, 172, 139, 4, 5, - 122, 123, 95, 140, 162, 92, 118, 118, 17, 93, - 92, 4, 119, 93, 92, 138, 126, 3, 116, 139, - 1, 42, 55, 56, 58, 59, 60, 66, 67, 71, - 74, 77, 82, 88, 95, 107, 130, 131, 132, 134, - 135, 136, 137, 140, 154, 111, 93, 4, 94, 142, - 92, 92, 141, 148, 93, 92, 3, 17, 3, 141, - 3, 17, 132, 172, 38, 137, 38, 38, 38, 38, - 131, 134, 128, 65, 3, 3, 105, 108, 125, 17, - 82, 3, 140, 3, 147, 140, 140, 147, 38, 109, - 3, 4, 95, 38, 92, 92, 92, 92, 92, 133, - 137, 154, 5, 108, 140, 132, 3, 132, 132, 3, - 3, 108, 122, 92, 62, 137, 3, 132, 3, 137, - 92, 132 + 39, 40, 41, 52, 53, 36, 93, 4, 33, 37, + 38, 106, 119, 120, 121, 122, 115, 17, 106, 116, + 117, 118, 172, 142, 167, 167, 95, 129, 106, 112, + 36, 37, 123, 124, 125, 126, 4, 93, 33, 38, + 119, 154, 154, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 141, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 173, 141, 142, 148, 149, 173, 36, + 128, 163, 139, 93, 120, 37, 38, 116, 140, 3, + 4, 17, 96, 130, 3, 4, 173, 140, 4, 5, + 123, 124, 96, 141, 163, 93, 119, 119, 17, 94, + 93, 4, 120, 94, 93, 139, 127, 3, 117, 140, + 1, 42, 55, 56, 58, 59, 60, 66, 67, 70, + 72, 75, 78, 83, 89, 96, 108, 131, 132, 133, + 135, 136, 137, 138, 141, 155, 112, 94, 4, 95, + 143, 93, 93, 142, 149, 94, 93, 3, 17, 3, + 142, 3, 17, 133, 173, 38, 38, 138, 38, 38, + 38, 38, 132, 135, 129, 65, 3, 3, 106, 109, + 126, 17, 83, 3, 141, 148, 3, 148, 141, 141, + 148, 38, 110, 3, 4, 96, 38, 93, 93, 93, + 93, 93, 93, 134, 138, 155, 5, 109, 141, 133, + 3, 3, 133, 133, 3, 3, 109, 123, 93, 62, + 138, 3, 133, 3, 138, 93, 133 }; #define yyerrok (yyerrstatus = 0) @@ -2598,251 +2601,258 @@ yyreduce: case 99: #line 539 "cc.y" { - (yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z); + (yyval.node) = new(OPREFETCH, (yyvsp[(3) - (5)].node), Z); } break; case 100: -#line 544 "cc.y" +#line 543 "cc.y" { - (yyval.node) = Z; + (yyval.node) = new(OSET, (yyvsp[(3) - (5)].node), Z); } break; - case 102: -#line 550 "cc.y" + case 101: +#line 548 "cc.y" { (yyval.node) = Z; } break; - case 104: -#line 557 "cc.y" + case 103: +#line 554 "cc.y" { - (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z); - (yyval.node)->type = types[TLONG]; + (yyval.node) = Z; } break; - case 106: -#line 565 "cc.y" + case 105: +#line 561 "cc.y" { - (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCAST, (yyvsp[(1) - (1)].node), Z); + (yyval.node)->type = types[TLONG]; } break; - case 108: -#line 572 "cc.y" + case 107: +#line 569 "cc.y" { - (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCOMMA, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: #line 576 "cc.y" { - (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: #line 580 "cc.y" { - (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: #line 584 "cc.y" { - (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: #line 588 "cc.y" { - (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: #line 592 "cc.y" { - (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 114: #line 596 "cc.y" { - (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 115: #line 600 "cc.y" { - (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 116: #line 604 "cc.y" { - (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 117: #line 608 "cc.y" { - (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 118: #line 612 "cc.y" { - (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 119: #line 616 "cc.y" { - (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 120: #line 620 "cc.y" { - (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 121: #line 624 "cc.y" { - (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 122: #line 628 "cc.y" { - (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 123: #line 632 "cc.y" { - (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 124: #line 636 "cc.y" { - (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 125: #line 640 "cc.y" { - (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 126: #line 644 "cc.y" { - (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node))); + (yyval.node) = new(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 127: #line 648 "cc.y" { - (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OCOND, (yyvsp[(1) - (5)].node), new(OLIST, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node))); } break; case 128: #line 652 "cc.y" { - (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OAS, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 129: #line 656 "cc.y" { - (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 130: #line 660 "cc.y" { - (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 131: #line 664 "cc.y" { - (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 132: #line 668 "cc.y" { - (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASDIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 133: #line 672 "cc.y" { - (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 134: #line 676 "cc.y" { - (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASASHL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 135: #line 680 "cc.y" { - (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASASHR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 136: #line 684 "cc.y" { - (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + (yyval.node) = new(OASAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 137: #line 688 "cc.y" { + (yyval.node) = new(OASXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); + } + break; + + case 138: +#line 692 "cc.y" + { (yyval.node) = new(OASOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 139: -#line 695 "cc.y" + case 140: +#line 699 "cc.y" { (yyval.node) = new(OCAST, (yyvsp[(5) - (5)].node), Z); dodecl(NODECL, CXXX, (yyvsp[(2) - (5)].type), (yyvsp[(3) - (5)].node)); @@ -2851,8 +2861,8 @@ yyreduce: } break; - case 140: -#line 702 "cc.y" + case 141: +#line 706 "cc.y" { (yyval.node) = new(OSTRUCT, (yyvsp[(6) - (7)].node), Z); dodecl(NODECL, CXXX, (yyvsp[(2) - (7)].type), (yyvsp[(3) - (7)].node)); @@ -2860,94 +2870,94 @@ yyreduce: } break; - case 142: -#line 711 "cc.y" - { - (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z); - } - break; - case 143: #line 715 "cc.y" { - (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OIND, (yyvsp[(2) - (2)].node), Z); } break; case 144: #line 719 "cc.y" { - (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OADDR, (yyvsp[(2) - (2)].node), Z); } break; case 145: #line 723 "cc.y" { - (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPOS, (yyvsp[(2) - (2)].node), Z); } break; case 146: #line 727 "cc.y" { - (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(ONEG, (yyvsp[(2) - (2)].node), Z); } break; case 147: #line 731 "cc.y" { - (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(ONOT, (yyvsp[(2) - (2)].node), Z); } break; case 148: #line 735 "cc.y" { - (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OCOM, (yyvsp[(2) - (2)].node), Z); } break; case 149: #line 739 "cc.y" { - (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPREINC, (yyvsp[(2) - (2)].node), Z); } break; case 150: #line 743 "cc.y" { - (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OPREDEC, (yyvsp[(2) - (2)].node), Z); } break; case 151: #line 747 "cc.y" { - (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); + (yyval.node) = new(OSIZE, (yyvsp[(2) - (2)].node), Z); } break; case 152: -#line 753 "cc.y" +#line 751 "cc.y" { - (yyval.node) = (yyvsp[(2) - (3)].node); + (yyval.node) = new(OSIGN, (yyvsp[(2) - (2)].node), Z); } break; case 153: #line 757 "cc.y" { + (yyval.node) = (yyvsp[(2) - (3)].node); + } + break; + + case 154: +#line 761 "cc.y" + { (yyval.node) = new(OSIZE, Z, Z); dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); (yyval.node)->type = lastdcl; } break; - case 154: -#line 763 "cc.y" + case 155: +#line 767 "cc.y" { (yyval.node) = new(OSIGN, Z, Z); dodecl(NODECL, CXXX, (yyvsp[(3) - (5)].type), (yyvsp[(4) - (5)].node)); @@ -2955,8 +2965,8 @@ yyreduce: } break; - case 155: -#line 769 "cc.y" + case 156: +#line 773 "cc.y" { (yyval.node) = new(OFUNC, (yyvsp[(1) - (4)].node), Z); if((yyvsp[(1) - (4)].node)->op == ONAME) @@ -2966,45 +2976,45 @@ yyreduce: } break; - case 156: -#line 777 "cc.y" + case 157: +#line 781 "cc.y" { (yyval.node) = new(OIND, new(OADD, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)), Z); } break; - case 157: -#line 781 "cc.y" + case 158: +#line 785 "cc.y" { (yyval.node) = new(ODOT, new(OIND, (yyvsp[(1) - (3)].node), Z), Z); (yyval.node)->sym = (yyvsp[(3) - (3)].sym); } break; - case 158: -#line 786 "cc.y" + case 159: +#line 790 "cc.y" { (yyval.node) = new(ODOT, (yyvsp[(1) - (3)].node), Z); (yyval.node)->sym = (yyvsp[(3) - (3)].sym); } break; - case 159: -#line 791 "cc.y" + case 160: +#line 795 "cc.y" { (yyval.node) = new(OPOSTINC, (yyvsp[(1) - (2)].node), Z); } break; - case 160: -#line 795 "cc.y" + case 161: +#line 799 "cc.y" { (yyval.node) = new(OPOSTDEC, (yyvsp[(1) - (2)].node), Z); } break; - case 162: -#line 800 "cc.y" + case 163: +#line 804 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TINT]; @@ -3013,8 +3023,8 @@ yyreduce: } break; - case 163: -#line 807 "cc.y" + case 164: +#line 811 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TLONG]; @@ -3023,8 +3033,8 @@ yyreduce: } break; - case 164: -#line 814 "cc.y" + case 165: +#line 818 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TUINT]; @@ -3033,8 +3043,8 @@ yyreduce: } break; - case 165: -#line 821 "cc.y" + case 166: +#line 825 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TULONG]; @@ -3043,8 +3053,8 @@ yyreduce: } break; - case 166: -#line 828 "cc.y" + case 167: +#line 832 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TDOUBLE]; @@ -3053,8 +3063,8 @@ yyreduce: } break; - case 167: -#line 835 "cc.y" + case 168: +#line 839 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TFLOAT]; @@ -3063,8 +3073,8 @@ yyreduce: } break; - case 168: -#line 842 "cc.y" + case 169: +#line 846 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TVLONG]; @@ -3073,8 +3083,8 @@ yyreduce: } break; - case 169: -#line 849 "cc.y" + case 170: +#line 853 "cc.y" { (yyval.node) = new(OCONST, Z, Z); (yyval.node)->type = types[TUVLONG]; @@ -3083,8 +3093,8 @@ yyreduce: } break; - case 172: -#line 860 "cc.y" + case 173: +#line 864 "cc.y" { (yyval.node) = new(OSTRING, Z, Z); (yyval.node)->type = typ(TARRAY, types[TCHAR]); @@ -3096,8 +3106,8 @@ yyreduce: } break; - case 173: -#line 870 "cc.y" + case 174: +#line 874 "cc.y" { char *s; int n; @@ -3115,8 +3125,8 @@ yyreduce: } break; - case 174: -#line 888 "cc.y" + case 175: +#line 892 "cc.y" { (yyval.node) = new(OLSTRING, Z, Z); (yyval.node)->type = typ(TARRAY, types[TUSHORT]); @@ -3128,8 +3138,8 @@ yyreduce: } break; - case 175: -#line 898 "cc.y" + case 176: +#line 902 "cc.y" { char *s; int n; @@ -3147,22 +3157,22 @@ yyreduce: } break; - case 176: -#line 915 "cc.y" + case 177: +#line 919 "cc.y" { (yyval.node) = Z; } break; - case 179: -#line 923 "cc.y" + case 180: +#line 927 "cc.y" { (yyval.node) = new(OLIST, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 180: -#line 929 "cc.y" + case 181: +#line 933 "cc.y" { (yyval.tyty).t1 = strf; (yyval.tyty).t2 = strl; @@ -3177,8 +3187,8 @@ yyreduce: } break; - case 181: -#line 942 "cc.y" + case 182: +#line 946 "cc.y" { (yyval.type) = strf; strf = (yyvsp[(2) - (4)].tyty).t1; @@ -3188,32 +3198,32 @@ yyreduce: } break; - case 182: -#line 951 "cc.y" + case 183: +#line 955 "cc.y" { lastclass = CXXX; lasttype = types[TINT]; } break; - case 184: -#line 959 "cc.y" + case 185: +#line 963 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (1)].type); (yyval.tycl).c = CXXX; } break; - case 185: -#line 964 "cc.y" + case 186: +#line 968 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); (yyval.tycl).c = CXXX; } break; - case 186: -#line 969 "cc.y" + case 187: +#line 973 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(1) - (1)].lval)); (yyval.tycl).c = simplec((yyvsp[(1) - (1)].lval)); @@ -3221,8 +3231,8 @@ yyreduce: } break; - case 187: -#line 975 "cc.y" + case 188: +#line 979 "cc.y" { (yyval.tycl).t = (yyvsp[(1) - (2)].type); (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); @@ -3232,8 +3242,8 @@ yyreduce: } break; - case 188: -#line 983 "cc.y" + case 189: +#line 987 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval))); (yyval.tycl).c = simplec((yyvsp[(2) - (2)].lval)); @@ -3241,8 +3251,8 @@ yyreduce: } break; - case 189: -#line 989 "cc.y" + case 190: +#line 993 "cc.y" { (yyval.tycl).t = (yyvsp[(2) - (3)].type); (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)); @@ -3250,8 +3260,8 @@ yyreduce: } break; - case 190: -#line 995 "cc.y" + case 191: +#line 999 "cc.y" { (yyval.tycl).t = simplet((yyvsp[(2) - (2)].lval)); (yyval.tycl).c = simplec((yyvsp[(1) - (2)].lval)); @@ -3259,8 +3269,8 @@ yyreduce: } break; - case 191: -#line 1001 "cc.y" + case 192: +#line 1005 "cc.y" { (yyval.tycl).t = simplet(typebitor((yyvsp[(2) - (3)].lval), (yyvsp[(3) - (3)].lval))); (yyval.tycl).c = simplec((yyvsp[(1) - (3)].lval)|(yyvsp[(3) - (3)].lval)); @@ -3268,8 +3278,8 @@ yyreduce: } break; - case 192: -#line 1009 "cc.y" + case 193: +#line 1013 "cc.y" { (yyval.type) = (yyvsp[(1) - (1)].tycl).t; if((yyvsp[(1) - (1)].tycl).c != CXXX) @@ -3277,31 +3287,31 @@ yyreduce: } break; - case 193: -#line 1017 "cc.y" + case 194: +#line 1021 "cc.y" { lasttype = (yyvsp[(1) - (1)].tycl).t; lastclass = (yyvsp[(1) - (1)].tycl).c; } break; - case 194: -#line 1024 "cc.y" + case 195: +#line 1028 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; } break; - case 195: -#line 1029 "cc.y" + case 196: +#line 1033 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TSTRUCT, autobn); } break; - case 196: -#line 1033 "cc.y" + case 197: +#line 1037 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; if((yyval.type)->link != T) @@ -3311,8 +3321,8 @@ yyreduce: } break; - case 197: -#line 1041 "cc.y" + case 198: +#line 1045 "cc.y" { taggen++; sprint(symb, "_%d_", taggen); @@ -3322,23 +3332,23 @@ yyreduce: } break; - case 198: -#line 1049 "cc.y" + case 199: +#line 1053 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; } break; - case 199: -#line 1054 "cc.y" + case 200: +#line 1058 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TUNION, autobn); } break; - case 200: -#line 1058 "cc.y" + case 201: +#line 1062 "cc.y" { (yyval.type) = (yyvsp[(2) - (4)].sym)->suetag; if((yyval.type)->link != T) @@ -3348,8 +3358,8 @@ yyreduce: } break; - case 201: -#line 1066 "cc.y" + case 202: +#line 1070 "cc.y" { taggen++; sprint(symb, "_%d_", taggen); @@ -3359,8 +3369,8 @@ yyreduce: } break; - case 202: -#line 1074 "cc.y" + case 203: +#line 1078 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, 0); (yyval.type) = (yyvsp[(2) - (2)].sym)->suetag; @@ -3370,23 +3380,23 @@ yyreduce: } break; - case 203: -#line 1082 "cc.y" + case 204: +#line 1086 "cc.y" { dotag((yyvsp[(2) - (2)].sym), TENUM, autobn); } break; - case 204: -#line 1086 "cc.y" + case 205: +#line 1090 "cc.y" { en.tenum = T; en.cenum = T; } break; - case 205: -#line 1091 "cc.y" + case 206: +#line 1095 "cc.y" { (yyval.type) = (yyvsp[(2) - (7)].sym)->suetag; if((yyval.type)->link != T) @@ -3400,167 +3410,167 @@ yyreduce: } break; - case 206: -#line 1103 "cc.y" + case 207: +#line 1107 "cc.y" { en.tenum = T; en.cenum = T; } break; - case 207: -#line 1108 "cc.y" + case 208: +#line 1112 "cc.y" { (yyval.type) = en.tenum; } break; - case 208: -#line 1112 "cc.y" + case 209: +#line 1116 "cc.y" { (yyval.type) = tcopy((yyvsp[(1) - (1)].sym)->type); } break; - case 210: -#line 1119 "cc.y" + case 211: +#line 1123 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 211: -#line 1124 "cc.y" + case 212: +#line 1128 "cc.y" { (yyval.lval) = 0; } break; - case 212: -#line 1128 "cc.y" + case 213: +#line 1132 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 217: -#line 1140 "cc.y" + case 218: +#line 1144 "cc.y" { (yyval.lval) = typebitor((yyvsp[(1) - (2)].lval), (yyvsp[(2) - (2)].lval)); } break; - case 220: -#line 1150 "cc.y" + case 221: +#line 1154 "cc.y" { doenum((yyvsp[(1) - (1)].sym), Z); } break; - case 221: -#line 1154 "cc.y" + case 222: +#line 1158 "cc.y" { doenum((yyvsp[(1) - (3)].sym), (yyvsp[(3) - (3)].node)); } break; - case 224: -#line 1161 "cc.y" + case 225: +#line 1165 "cc.y" { (yyval.lval) = BCHAR; } break; - case 225: -#line 1162 "cc.y" + case 226: +#line 1166 "cc.y" { (yyval.lval) = BSHORT; } break; - case 226: -#line 1163 "cc.y" + case 227: +#line 1167 "cc.y" { (yyval.lval) = BINT; } break; - case 227: -#line 1164 "cc.y" + case 228: +#line 1168 "cc.y" { (yyval.lval) = BLONG; } break; - case 228: -#line 1165 "cc.y" + case 229: +#line 1169 "cc.y" { (yyval.lval) = BSIGNED; } break; - case 229: -#line 1166 "cc.y" + case 230: +#line 1170 "cc.y" { (yyval.lval) = BUNSIGNED; } break; - case 230: -#line 1167 "cc.y" + case 231: +#line 1171 "cc.y" { (yyval.lval) = BFLOAT; } break; - case 231: -#line 1168 "cc.y" + case 232: +#line 1172 "cc.y" { (yyval.lval) = BDOUBLE; } break; - case 232: -#line 1169 "cc.y" + case 233: +#line 1173 "cc.y" { (yyval.lval) = BVOID; } break; - case 233: -#line 1172 "cc.y" + case 234: +#line 1176 "cc.y" { (yyval.lval) = BAUTO; } break; - case 234: -#line 1173 "cc.y" + case 235: +#line 1177 "cc.y" { (yyval.lval) = BSTATIC; } break; - case 235: -#line 1174 "cc.y" + case 236: +#line 1178 "cc.y" { (yyval.lval) = BEXTERN; } break; - case 236: -#line 1175 "cc.y" + case 237: +#line 1179 "cc.y" { (yyval.lval) = BTYPEDEF; } break; - case 237: -#line 1176 "cc.y" + case 238: +#line 1180 "cc.y" { (yyval.lval) = BTYPESTR; } break; - case 238: -#line 1177 "cc.y" + case 239: +#line 1181 "cc.y" { (yyval.lval) = BREGISTER; } break; - case 239: -#line 1178 "cc.y" + case 240: +#line 1182 "cc.y" { (yyval.lval) = 0; } break; - case 240: -#line 1181 "cc.y" + case 241: +#line 1185 "cc.y" { (yyval.lval) = BCONSTNT; } break; - case 241: -#line 1182 "cc.y" + case 242: +#line 1186 "cc.y" { (yyval.lval) = BVOLATILE; } break; - case 242: -#line 1183 "cc.y" + case 243: +#line 1187 "cc.y" { (yyval.lval) = 0; } break; - case 243: -#line 1187 "cc.y" + case 244: +#line 1191 "cc.y" { (yyval.node) = new(ONAME, Z, Z); if((yyvsp[(1) - (1)].sym)->class == CLOCAL) @@ -3576,8 +3586,8 @@ yyreduce: } break; - case 244: -#line 1202 "cc.y" + case 245: +#line 1206 "cc.y" { (yyval.node) = new(ONAME, Z, Z); (yyval.node)->sym = (yyvsp[(1) - (1)].sym); @@ -3592,7 +3602,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 3596 "y.tab.c" +#line 3606 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -3806,6 +3816,6 @@ yyreturn: } -#line 1215 "cc.y" +#line 1219 "cc.y" diff --git a/src/cmd/cc/y.tab.h b/src/cmd/cc/y.tab.h index c56a1d85a..32daca9b6 100644 --- a/src/cmd/cc/y.tab.h +++ b/src/cmd/cc/y.tab.h @@ -88,28 +88,29 @@ LIF = 304, LINT = 305, LLONG = 306, - LREGISTER = 307, - LRETURN = 308, - LSHORT = 309, - LSIZEOF = 310, - LUSED = 311, - LSTATIC = 312, - LSTRUCT = 313, - LSWITCH = 314, - LTYPEDEF = 315, - LTYPESTR = 316, - LUNION = 317, - LUNSIGNED = 318, - LWHILE = 319, - LVOID = 320, - LENUM = 321, - LSIGNED = 322, - LCONSTNT = 323, - LVOLATILE = 324, - LSET = 325, - LSIGNOF = 326, - LRESTRICT = 327, - LINLINE = 328 + LPREFETCH = 307, + LREGISTER = 308, + LRETURN = 309, + LSHORT = 310, + LSIZEOF = 311, + LUSED = 312, + LSTATIC = 313, + LSTRUCT = 314, + LSWITCH = 315, + LTYPEDEF = 316, + LTYPESTR = 317, + LUNION = 318, + LUNSIGNED = 319, + LWHILE = 320, + LVOID = 321, + LENUM = 322, + LSIGNED = 323, + LCONSTNT = 324, + LVOLATILE = 325, + LSET = 326, + LSIGNOF = 327, + LRESTRICT = 328, + LINLINE = 329 }; #endif /* Tokens. */ @@ -162,28 +163,29 @@ #define LIF 304 #define LINT 305 #define LLONG 306 -#define LREGISTER 307 -#define LRETURN 308 -#define LSHORT 309 -#define LSIZEOF 310 -#define LUSED 311 -#define LSTATIC 312 -#define LSTRUCT 313 -#define LSWITCH 314 -#define LTYPEDEF 315 -#define LTYPESTR 316 -#define LUNION 317 -#define LUNSIGNED 318 -#define LWHILE 319 -#define LVOID 320 -#define LENUM 321 -#define LSIGNED 322 -#define LCONSTNT 323 -#define LVOLATILE 324 -#define LSET 325 -#define LSIGNOF 326 -#define LRESTRICT 327 -#define LINLINE 328 +#define LPREFETCH 307 +#define LREGISTER 308 +#define LRETURN 309 +#define LSHORT 310 +#define LSIZEOF 311 +#define LUSED 312 +#define LSTATIC 313 +#define LSTRUCT 314 +#define LSWITCH 315 +#define LTYPEDEF 316 +#define LTYPESTR 317 +#define LUNION 318 +#define LUNSIGNED 319 +#define LWHILE 320 +#define LVOID 321 +#define LENUM 322 +#define LSIGNED 323 +#define LCONSTNT 324 +#define LVOLATILE 325 +#define LSET 326 +#define LSIGNOF 327 +#define LRESTRICT 328 +#define LINLINE 329 @@ -217,7 +219,7 @@ typedef union YYSTYPE vlong vval; } /* Line 1529 of yacc.c. */ -#line 221 "y.tab.h" +#line 223 "y.tab.h" YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go index 381e606ef..dbae3b7b1 100644 --- a/src/cmd/cgo/ast.go +++ b/src/cmd/cgo/ast.go @@ -78,7 +78,7 @@ func (f *File) ReadGo(name string) { } if cg != nil { f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name) - f.Preamble += cg.Text() + "\n" + f.Preamble += commentText(cg) + "\n" } } } @@ -131,6 +131,30 @@ func (f *File) ReadGo(name string) { f.AST = ast2 } +// Like ast.CommentGroup's Text method but preserves +// leading blank lines, so that line numbers line up. +func commentText(g *ast.CommentGroup) string { + if g == nil { + return "" + } + var pieces []string + for _, com := range g.List { + c := string(com.Text) + // Remove comment markers. + // The parser has given us exactly the comment text. + switch c[1] { + case '/': + //-style comment (no newline at the end) + c = c[2:] + "\n" + case '*': + /*-style comment */ + c = c[2 : len(c)-2] + } + pieces = append(pieces, c) + } + return strings.Join(pieces, "") +} + // Save references to C.xxx for later processing. func (f *File) saveRef(x interface{}, context string) { n, ok := x.(*ast.Expr) diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 1bb48f44e..955b7c495 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -65,11 +65,13 @@ struct_, union_, or enum_, as in C.struct_stat. Go structs cannot embed fields with C types. -Any C function that returns a value may be called in a multiple -assignment context to retrieve both the return value and the -C errno variable as an error. For example: +Any C function (even void functions) may be called in a multiple +assignment context to retrieve both the return value (if any) and the +C errno variable as an error (use _ to skip the result value if the +function returns void). For example: n, err := C.atoi("abc") + _, err := C.voidFunc() In C, a function argument written as a fixed size array actually requires a pointer to the first element of the array. @@ -83,7 +85,8 @@ by making copies of the data. In pseudo-Go definitions: // Go string to 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. + // freed, such as by calling C.free (be sure to include stdlib.h + // if C.free is needed). func C.CString(string) *C.char // C string to Go string @@ -113,6 +116,11 @@ copied from the cgo input files. Functions with multiple return values are mapped to functions returning a struct. Not all Go types can be mapped to C types in a useful way. +Using //export in a file places a restriction on the preamble: +since it is copied into two different C output files, it must not +contain any definitions, only declarations. Definitions must be +placed in preambles in other files, or in C source files. + Cgo transforms the input file into four output files: two Go source files, a C file for 6c (or 8c or 5c), and a C file for gcc. @@ -125,4 +133,492 @@ Cgo does not yet work with gccgo. See "C? Go? Cgo!" for an introduction to using cgo: http://golang.org/doc/articles/c_go_cgo.html */ -package documentation +package main + +/* +Implementation details. + +Cgo provides a way for Go programs to call C code linked into the same +address space. This comment explains the operation of cgo. + +Cgo reads a set of Go source files and looks for statements saying +import "C". If the import has a doc comment, that comment is +taken as literal C code to be used as a preamble to any C code +generated by cgo. A typical preamble #includes necessary definitions: + + // #include <stdio.h> + import "C" + +For more details about the usage of cgo, see the documentation +comment at the top of this file. + +Understanding C + +Cgo scans the Go source files that import "C" for uses of that +package, such as C.puts. It collects all such identifiers. The next +step is to determine each kind of name. In C.xxx the xxx might refer +to a type, a function, a constant, or a global variable. Cgo must +decide which. + +The obvious thing for cgo to do is to process the preamble, expanding +#includes and processing the corresponding C code. That would require +a full C parser and type checker that was also aware of any extensions +known to the system compiler (for example, all the GNU C extensions) as +well as the system-specific header locations and system-specific +pre-#defined macros. This is certainly possible to do, but it is an +enormous amount of work. + +Cgo takes a different approach. It determines the meaning of C +identifiers not by parsing C code but by feeding carefully constructed +programs into the system C compiler and interpreting the generated +error messages, debug information, and object files. In practice, +parsing these is significantly less work and more robust than parsing +C source. + +Cgo first invokes gcc -E -dM on the preamble, in order to find out +about simple #defines for constants and the like. These are recorded +for later use. + +Next, cgo needs to identify the kinds for each identifier. For the +identifiers C.foo and C.bar, cgo generates this C program: + + <preamble> + void __cgo__f__(void) { + #line 1 "cgo-test" + foo; + enum { _cgo_enum_0 = foo }; + bar; + enum { _cgo_enum_1 = bar }; + } + +This program will not compile, but cgo can look at the error messages +to infer the kind of each identifier. The line number given in the +error tells cgo which identifier is involved. + +An error like "unexpected type name" or "useless type name in empty +declaration" or "declaration does not declare anything" tells cgo that +the identifier is a type. + +An error like "statement with no effect" or "expression result unused" +tells cgo that the identifier is not a type, but not whether it is a +constant, function, or global variable. + +An error like "not an integer constant" tells cgo that the identifier +is not a constant. If it is also not a type, it must be a function or +global variable. For now, those can be treated the same. + +Next, cgo must learn the details of each type, variable, function, or +constant. It can do this by reading object files. If cgo has decided +that t1 is a type, v2 and v3 are variables or functions, and c4, c5, +and c6 are constants, it generates: + + <preamble> + typeof(t1) *__cgo__1; + typeof(v2) *__cgo__2; + typeof(v3) *__cgo__3; + typeof(c4) *__cgo__4; + enum { __cgo_enum__4 = c4 }; + typeof(c5) *__cgo__5; + enum { __cgo_enum__5 = c5 }; + typeof(c6) *__cgo__6; + enum { __cgo_enum__6 = c6 }; + + long long __cgo_debug_data[] = { + 0, // t1 + 0, // v2 + 0, // v3 + c4, + c5, + c6, + 1 + }; + +and again invokes the system C compiler, to produce an object file +containing debug information. Cgo parses the DWARF debug information +for __cgo__N to learn the type of each identifier. (The types also +distinguish functions from global variables.) If using a standard gcc, +cgo can parse the DWARF debug information for the __cgo_enum__N to +learn the identifier's value. The LLVM-based gcc on OS X emits +incomplete DWARF information for enums; in that case cgo reads the +constant values from the __cgo_debug_data from the object file's data +segment. + +At this point cgo knows the meaning of each C.xxx well enough to start +the translation process. + +Translating Go + +[The rest of this comment refers to 6g and 6c, the Go and C compilers +that are part of the amd64 port of the gc Go toolchain. Everything here +applies to another architecture's compilers as well.] + +Given the input Go files x.go and y.go, cgo generates these source +files: + + x.cgo1.go # for 6g + y.cgo1.go # for 6g + _cgo_gotypes.go # for 6g + _cgo_defun.c # for 6c + x.cgo2.c # for gcc + y.cgo2.c # for gcc + _cgo_export.c # for gcc + _cgo_main.c # for gcc + +The file x.cgo1.go is a copy of x.go with the import "C" removed and +references to C.xxx replaced with names like _Cfunc_xxx or _Ctype_xxx. +The definitions of those identifiers, written as Go functions, types, +or variables, are provided in _cgo_gotypes.go. + +Here is a _cgo_gotypes.go containing definitions for C.flush (provided +in the preamble) and C.puts (from stdio): + + type _Ctype_char int8 + type _Ctype_int int32 + type _Ctype_void [0]byte + + func _Cfunc_CString(string) *_Ctype_char + func _Cfunc_flush() _Ctype_void + func _Cfunc_puts(*_Ctype_char) _Ctype_int + +For functions, cgo only writes an external declaration in the Go +output. The implementation is in a combination of C for 6c (meaning +any gc-toolchain compiler) and C for gcc. + +The 6c file contains the definitions of the functions. They all have +similar bodies that invoke runtime·cgocall to make a switch from the +Go runtime world to the system C (GCC-based) world. + +For example, here is the definition of _Cfunc_puts: + + void _cgo_be59f0f25121_Cfunc_puts(void*); + + void + ·_Cfunc_puts(struct{uint8 x[1];}p) + { + runtime·cgocall(_cgo_be59f0f25121_Cfunc_puts, &p); + } + +The hexadecimal number is a hash of cgo's input, chosen to be +deterministic yet unlikely to collide with other uses. The actual +function _cgo_be59f0f25121_Cfunc_puts is implemented in a C source +file compiled by gcc, the file x.cgo2.c: + + void + _cgo_be59f0f25121_Cfunc_puts(void *v) + { + struct { + char* p0; + int r; + char __pad12[4]; + } __attribute__((__packed__)) *a = v; + a->r = puts((void*)a->p0); + } + +It extracts the arguments from the pointer to _Cfunc_puts's argument +frame, invokes the system C function (in this case, puts), stores the +result in the frame, and returns. + +Linking + +Once the _cgo_export.c and *.cgo2.c files have been compiled with gcc, +they need to be linked into the final binary, along with the libraries +they might depend on (in the case of puts, stdio). 6l has been +extended to understand basic ELF files, but it does not understand ELF +in the full complexity that modern C libraries embrace, so it cannot +in general generate direct references to the system libraries. + +Instead, the build process generates an object file using dynamic +linkage to the desired libraries. The main function is provided by +_cgo_main.c: + + int main() { return 0; } + void crosscall2(void(*fn)(void*, int), void *a, int c) { } + void _cgo_allocate(void *a, int c) { } + void _cgo_panic(void *a, int c) { } + +The extra functions here are stubs to satisfy the references in the C +code generated for gcc. The build process links this stub, along with +_cgo_export.c and *.cgo2.c, into a dynamic executable and then lets +cgo examine the executable. Cgo records the list of shared library +references and resolved names and writes them into a new file +_cgo_import.c, which looks like: + + #pragma cgo_dynamic_linker "/lib64/ld-linux-x86-64.so.2" + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic __libc_start_main __libc_start_main#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic stdout stdout#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic fflush fflush#GLIBC_2.2.5 "libc.so.6" + #pragma cgo_import_dynamic _ _ "libpthread.so.0" + #pragma cgo_import_dynamic _ _ "libc.so.6" + +In the end, the compiled Go package, which will eventually be +presented to 6l as part of a larger program, contains: + + _go_.6 # 6g-compiled object for _cgo_gotypes.go *.cgo1.go + _cgo_defun.6 # 6c-compiled object for _cgo_defun.c + _all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c + _cgo_import.6 # 6c-compiled object for _cgo_import.c + +The final program will be a dynamic executable, so that 6l 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 +limited in the ELF or other features that they use. + +In essence, the _cgo_import.6 file includes the extra linking +directives that 6l is not sophisticated enough to derive from _all.o +on its own. Similarly, the _all.o uses dynamic references to real +system object code because 6l is not sophisticated enough to process +the real code. + +The main benefits of this system are that 6l remains relatively simple +(it does not need to implement a complete ELF and Mach-O linker) and +that gcc is not needed after the package is compiled. For example, +package net uses cgo for access to name resolution functions provided +by libc. Although gcc is needed to compile package net, gcc is not +needed to link programs that import package net. + +Runtime + +When using cgo, Go must not assume that it owns all details of the +process. In particular it needs to coordinate with C in the use of +threads and thread-local storage. The runtime package, in its own +(6c-compiled) C code, declares a few uninitialized (default bss) +variables: + + bool runtime·iscgo; + void (*libcgo_thread_start)(void*); + void (*initcgo)(G*); + +Any package using cgo imports "runtime/cgo", which provides +initializations for these variables. It sets iscgo to 1, initcgo to a +gcc-compiled function that can be called early during program startup, +and libcgo_thread_start to a gcc-compiled function that can be used to +create a new thread, in place of the runtime's usual direct system +calls. + +[NOTE: From here down is planned but not yet implemented.] + +Internal and External Linking + +The text above describes "internal" linking, in which 6l parses and +links host object files (ELF, Mach-O, PE, and so on) into the final +executable itself. Keeping 6l simple means we cannot possibly +implement the full semantics of the host linker, so the kinds of +objects that can be linked directly into the binary is limited (other +code can only be used as a dynamic library). On the other hand, when +using internal linking, 6l can generate Go binaries by itself. + +In order to allow linking arbitrary object files without requiring +dynamic libraries, cgo will soon support an "external" linking mode +too. In external linking mode, 6l does not process any host object +files. Instead, it collects all the Go code and writes a single go.o +object file containing it. Then it invokes the host linker (usually +gcc) to combine the go.o object file and any supporting non-Go code +into a final executable. External linking avoids the dynamic library +requirement but introduces a requirement that the host linker be +present to create such a binary. + +Most builds both compile source code and invoke the linker to create a +binary. When cgo is involved, the compile step already requires gcc, so +it is not problematic for the link step to require gcc too. + +An important exception is builds using a pre-compiled copy of the +standard library. In particular, package net uses cgo on most systems, +and we want to preserve the ability to compile pure Go code that +imports net without requiring gcc to be present at link time. (In this +case, the dynamic library requirement is less significant, because the +only library involved is libc.so, which can usually be assumed +present.) + +This conflict between functionality and the gcc requirement means we +must support both internal and external linking, depending on the +circumstances: if net is the only cgo-using package, then internal +linking is probably fine, but if other packages are involved, so that there +are dependencies on libraries beyond libc, external linking is likely +to work better. The compilation of a package records the relevant +information to support both linking modes, leaving the decision +to be made when linking the final binary. + +Linking Directives + +In either linking mode, package-specific directives must be passed +through to 6l. These are communicated by writing #pragma directives +in a C source file compiled by 6c. The directives are copied into the .6 object file +and then processed by the linker. + +The directives are: + +#pragma cgo_import_dynamic <local> [<remote> ["<library>"]] + + In internal linking mode, allow an unresolved reference to + <local>, assuming it will be resolved by a dynamic library + symbol. The optional <remote> specifies the symbol's name and + possibly version in the dynamic library, and the optional "<library>" + names the specific library where the symbol should be found. + + In the <remote>, # or @ can be used to introduce a symbol version. + + Examples: + #pragma cgo_import_dynamic puts + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 + #pragma cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6" + + A side effect of the cgo_dynamic_import directive with a + library is to make the final binary depend on that dynamic + library. To get the dependency without importing any specific + symbols, use _ for local and remote. + + Example: + #pragma cgo_import_dynamic _ _ "libc.so.6" + + For compatibility with current versions of SWIG, + #pragma dynimport is an alias for #pragma cgo_dynamic_import. + +#pragma cgo_dynamic_linker "<path>" + + In internal linking mode, use "<path>" as the dynamic linker + in the final binary. This directive is only needed from one + package when constructing a binary; by convention it is + supplied by runtime/cgo. + + Example: + #pragma cgo_dynamic_linker "/lib/ld-linux.so.2" + +#pragma cgo_export <local> <remote> + + In both internal and external linking modes, put the Go symbol + named <local> into the program's exported symbol table as + <remote>, so that C code can refer to it by that name. This + mechanism makes it possible for C code to call back into Go or + to share Go's data. + + For compatibility with current versions of SWIG, + #pragma dynexport is an alias for #pragma cgo_export. + +#pragma cgo_import_static <local> + + In external linking mode, allow unresolved references to + <local> in the go.o object file prepared for the host linker, + under the assumption that <local> will be supplied by the + other object files that will be linked with go.o. + + Example: + #pragma cgo_import_static puts_wrapper + +#pragma cgo_ldflag "<arg>" + + In external linking mode, invoke the host linker (usually gcc) + with "<arg>" as a command-line argument following the .o files. + Note that the arguments are for "gcc", not "ld". + + Example: + #pragma cgo_ldflag "-lpthread" + #pragma cgo_ldflag "-L/usr/local/sqlite3/lib" + +A package compiled with cgo will include directives for both +internal and external linking; the linker will select the appropriate +subset for the chosen linking mode. + +Example + +As a simple example, consider a package that uses cgo to call C.sin. +The following code will be generated by cgo: + + // compiled by 6g + + type _Ctype_double float64 + func _Cfunc_sin(_Ctype_double) _Ctype_double + + // compiled by 6c + + #pragma cgo_import_dynamic sin sin#GLIBC_2.2.5 "libm.so.6" + + #pragma cgo_import_static _cgo_gcc_Cfunc_sin + #pragma cgo_ldflag "-lm" + + void _cgo_gcc_Cfunc_sin(void*); + + void + ·_Cfunc_sin(struct{uint8 x[16];}p) + { + runtime·cgocall(_cgo_gcc_Cfunc_sin, &p); + } + + // compiled by gcc, into foo.cgo2.o + + void + _cgo_gcc_Cfunc_sin(void *v) + { + struct { + double p0; + double r; + } __attribute__((__packed__)) *a = v; + a->r = sin(a->p0); + } + +What happens at link time depends on whether the final binary is linked +using the internal or external mode. If other packages are compiled in +"external only" mode, then the final link will be an external one. +Otherwise the link will be an internal one. + +The directives in the 6c-compiled file are used according to the kind +of final link used. + +In internal mode, 6l itself processes all the host object files, in +particular foo.cgo2.o. To do so, it uses the cgo_dynamic_import and +cgo_dynamic_linker directives to learn that the otherwise undefined +reference to sin in foo.cgo2.o should be rewritten to refer to the +symbol sin with version GLIBC_2.2.5 from the dynamic library +"libm.so.6", and the binary should request "/lib/ld-linux.so.2" as its +runtime dynamic linker. + +In external mode, 6l does not process any host object files, in +particular foo.cgo2.o. It links together the 6g- and 6c-generated +object files, along with any other Go code, into a go.o file. While +doing that, 6l will discover that there is no definition for +_cgo_gcc_Cfunc_sin, referred to by the 6c-compiled source file. This +is okay, because 6l also processes the cgo_import_static directive and +knows that _cgo_gcc_Cfunc_sin is expected to be supplied by a host +object file, so 6l does not treat the missing symbol as an error when +creating go.o. Indeed, the definition for _cgo_gcc_Cfunc_sin will be +provided to the host linker by foo2.cgo.o, which in turn will need the +symbol 'sin'. 6l also processes the cgo_ldflag directives, so that it +knows that the eventual host link command must include the -lm +argument, so that the host linker will be able to find 'sin' in the +math library. + +6l Command Line Interface + +The go command and any other Go-aware build systems invoke 6l +to link a collection of packages into a single binary. By default, 6l will +present the same interface it does today: + + 6l main.a + +produces a file named 6.out, even if 6l does so by invoking the host +linker in external linking mode. + +By default, 6l will decide the linking mode as follows: if the only +packages using cgo are those on a whitelist of standard library +packages (net, os/user, runtime/cgo), 6l will use internal linking +mode. Otherwise, there are non-standard cgo packages involved, and 6l +will use external linking mode. The first rule means that a build of +the godoc binary, which uses net but no other cgo, can run without +needing gcc available. The second rule means that a build of a +cgo-wrapped library like sqlite3 can generate a standalone executable +instead of needing to refer to a dynamic library. The specific choice +can be overridden using a command line flag: 6l -cgolink=internal or +6l -cgolink=external. + +In an external link, 6l will create a temporary directory, write any +host object files found in package archives to that directory (renamed +to avoid conflicts), write the go.o file to that directory, and invoke +the host linker. The default value for the host linker is $CC, split +into fields, or else "gcc". The specific host linker command line can +be overridden using a command line flag: 6l -hostld='gcc -ggdb' + +These defaults mean that Go-aware build systems can ignore the linking +changes and keep running plain '6l' and get reasonable results, but +they can also control the linking details if desired. + +*/ diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 98a847e6f..4b0a521a8 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -391,9 +391,9 @@ func (p *Package) guessKinds(f *File) []*Name { b.WriteString(builtinProlog) b.WriteString(f.Preamble) b.WriteString("void __cgo__f__(void) {\n") - b.WriteString("#line 0 \"cgo-test\"\n") + b.WriteString("#line 1 \"cgo-test\"\n") for i, n := range toSniff { - fmt.Fprintf(&b, "%s; enum { _cgo_enum_%d = %s }; /* cgo-test:%d */\n", n.C, i, n.C, i) + fmt.Fprintf(&b, "%s; /* #%d */\nenum { _cgo_enum_%d = %s }; /* #%d */\n", n.C, i, i, n.C, i) } b.WriteString("}\n") stderr := p.gccErrors(b.Bytes()) @@ -423,14 +423,18 @@ func (p *Package) guessKinds(f *File) []*Name { if err != nil { continue } + i = (i - 1) / 2 what := "" switch { default: continue - case strings.Contains(line, ": useless type name in empty declaration"): + case strings.Contains(line, ": useless type name in empty declaration"), + strings.Contains(line, ": declaration does not declare anything"), + strings.Contains(line, ": unexpected type name"): what = "type" isConst[i] = false - case strings.Contains(line, ": statement with no effect"): + case strings.Contains(line, ": statement with no effect"), + strings.Contains(line, ": expression result unused"): what = "not-type" // const or func or var case strings.Contains(line, "undeclared"): error_(token.NoPos, "%s", strings.TrimSpace(line[colon+1:])) @@ -508,7 +512,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) { fmt.Fprintf(&b, "\t0,\n") } } - fmt.Fprintf(&b, "\t0\n") + // for the last entry, we can not use 0, otherwise + // in case all __cgodebug_data is zero initialized, + // LLVM-based gcc will place the it in the __DATA.__common + // zero-filled section (our debug/macho doesn't support + // this) + fmt.Fprintf(&b, "\t1\n") fmt.Fprintf(&b, "};\n") d, bo, debugData := p.gccDebug(b.Bytes()) @@ -596,7 +605,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Record types and typedef information. var conv typeConv - conv.Init(p.PtrSize) + conv.Init(p.PtrSize, p.IntSize) for i, n := range names { if types[i] == nil { continue @@ -618,7 +627,9 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // Remove injected enum to ensure the value will deep-compare // equally in future loads of the same constant. delete(n.Type.EnumValues, k) - } else if n.Kind == "const" && i < len(enumVal) { + } + // Prefer debug data over DWARF debug output, if we have it. + if n.Kind == "const" && i < len(enumVal) { n.Const = fmt.Sprintf("%#x", enumVal[i]) } } @@ -637,7 +648,14 @@ func (p *Package) rewriteRef(f *File) { n.Kind = "var" } if n.Mangle == "" { - n.Mangle = "_C" + n.Kind + "_" + n.Go + // When using gccgo variables have to be + // exported so that they become global symbols + // that the C code can refer to. + prefix := "_C" + if *gccgo && n.Kind == "var" { + prefix = "C" + } + n.Mangle = prefix + n.Kind + "_" + n.Go } } @@ -662,9 +680,6 @@ func (p *Package) rewriteRef(f *File) { break } if r.Context == "call2" { - if r.Name.FuncType.Result == nil { - error_(r.Pos(), "assignment count mismatch: 2 = 0") - } // Invent new Name for the two-result function. n := f.Name["2"+r.Name.Go] if n == nil { @@ -720,23 +735,30 @@ func (p *Package) rewriteRef(f *File) { } } -// gccName returns the name of the compiler to run. Use $GCC if set in +// gccName returns the name of the compiler to run. Use $CC if set in // the environment, otherwise just "gcc". -func (p *Package) gccName() (ret string) { - if ret = os.Getenv("GCC"); ret == "" { - ret = "gcc" +func (p *Package) gccName() string { + // Use $CC if set, since that's what the build uses. + if ret := os.Getenv("CC"); ret != "" { + return ret } - return + // Fall back to $GCC if set, since that's what we used to use. + if ret := os.Getenv("GCC"); ret != "" { + return ret + } + return "gcc" } -// gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". +// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm". func (p *Package) gccMachine() []string { switch goarch { case "amd64": return []string{"-m64"} case "386": return []string{"-m32"} + case "arm": + return []string{"-marm"} // not thumb } return nil } @@ -755,9 +777,22 @@ func (p *Package) gccCmd() []string { "-o" + gccTmp(), // write object to tmp "-gdwarf-2", // generate DWARF v2 debugging symbols "-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise - "-c", // do not link - "-xc", // input language is C + "-c", // do not link + "-xc", // input language is C + } + if strings.Contains(p.gccName(), "clang") { + c = append(c, + "-ferror-limit=0", + // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) + // doesn't have -Wno-unneeded-internal-declaration, so we need yet another + // flag to disable the warning. Yes, really good diagnostics, clang. + "-Wno-unknown-warning-option", + "-Wno-unneeded-internal-declaration", + "-Wno-unused-function", + "-Qunused-arguments", + ) } + c = append(c, p.GccOptions...) c = append(c, p.gccMachine()...) c = append(c, "-") //read input from standard input @@ -795,15 +830,30 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) return d, f.ByteOrder, data } - // Can skip debug data block in ELF and PE for now. - // The DWARF information is complete. - if f, err := elf.Open(gccTmp()); err == nil { d, err := f.DWARF() if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, f.ByteOrder, nil + var data []byte + symtab, err := f.Symbols() + if err == nil { + for i := range symtab { + s := &symtab[i] + if s.Name == "__cgodebug_data" { + // 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 { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value-sect.Addr:] + } + } + } + } + } + } + return d, f.ByteOrder, data } if f, err := pe.Open(gccTmp()); err == nil { @@ -811,7 +861,20 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) if err != nil { fatalf("cannot load DWARF output from %s: %v", gccTmp(), err) } - return d, binary.LittleEndian, nil + var data []byte + for _, s := range f.Symbols { + if s.Name == "_"+"__cgodebug_data" { + if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) { + sect := f.Sections[i] + if s.Value < sect.Size { + if sdat, err := sect.Data(); err == nil { + data = sdat[s.Value:] + } + } + } + } + } + return d, binary.LittleEndian, data } fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp()) @@ -889,16 +952,19 @@ type typeConv struct { void ast.Expr unsafePointer ast.Expr string ast.Expr + goVoid ast.Expr // _Ctype_void, denotes C's void ptrSize int64 + intSize int64 } var tagGen int var typedef = make(map[string]*Type) var goIdent = make(map[string]*ast.Ident) -func (c *typeConv) Init(ptrSize int64) { +func (c *typeConv) Init(ptrSize, intSize int64) { c.ptrSize = ptrSize + c.intSize = intSize c.m = make(map[dwarf.Type]*Type) c.bool = c.Ident("bool") c.byte = c.Ident("byte") @@ -918,6 +984,7 @@ func (c *typeConv) Init(ptrSize int64) { c.unsafePointer = c.Ident("unsafe.Pointer") c.void = c.Ident("void") c.string = c.Ident("string") + c.goVoid = c.Ident("_Ctype_void") } // base strips away qualifiers and typedefs to get the underlying type @@ -988,6 +1055,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { return t } + // clang won't generate DW_AT_byte_size for pointer types, + // so we have to fix it here. + if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 { + dt.ByteSize = c.ptrSize + } + t := new(Type) t.Size = dtype.Size() t.Align = -1 @@ -1032,7 +1105,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { case *dwarf.BoolType: t.Go = c.bool - t.Align = c.ptrSize + t.Align = 1 case *dwarf.CharType: if t.Size != 1 { @@ -1163,11 +1236,12 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = name // publish before recursive calls goIdent[name.Name] = name switch dt.Kind { - case "union", "class": + case "class", "union": t.Go = c.Opaque(t.Size) if t.C.Empty() { t.C.Set("typeof(unsigned char[%d])", t.Size) } + t.Align = 1 // TODO: should probably base this on field alignment. typedef[name.Name] = t case "struct": g, csyntax, align := c.Struct(dt, pos) @@ -1245,8 +1319,9 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } case *dwarf.VoidType: - t.Go = c.void + t.Go = c.goVoid t.C.Set("void") + t.Align = 1 } switch dtype.(type) { @@ -1334,7 +1409,9 @@ func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType { } var r *Type var gr []*ast.Field - if _, ok := dtype.ReturnType.(*dwarf.VoidType); !ok && dtype.ReturnType != nil { + if _, ok := dtype.ReturnType.(*dwarf.VoidType); ok { + gr = []*ast.Field{{Type: c.goVoid}} + } else if dtype.ReturnType != nil { r = c.Type(dtype.ReturnType, pos) gr = []*ast.Field{{Type: r.Go}} } @@ -1493,8 +1570,8 @@ func godefsFields(fld []*ast.Field) { npad := 0 for _, f := range fld { for _, n := range f.Names { - if strings.HasPrefix(n.Name, prefix) && n.Name != prefix { - n.Name = n.Name[len(prefix):] + if n.Name != prefix { + n.Name = strings.TrimPrefix(n.Name, prefix) } if n.Name == "_" { // Use exported name instead. @@ -1522,7 +1599,7 @@ func godefsFields(fld []*ast.Field) { } // fieldPrefix returns the prefix that should be removed from all the -// field names when generating the C or Go code. For generated +// field names when generating the C or Go code. For generated // C, we leave the names as is (tv_sec, tv_usec), since that's what // people are used to seeing in C. For generated Go code, such as // package syscall's data structures, we drop a common prefix @@ -1539,7 +1616,7 @@ func fieldPrefix(fld []*ast.Field) string { // named, say, _pad in an otherwise prefixed header. // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we // still want to remove the tv_ prefix. - // The check for "orig_" here handles orig_eax in the + // The check for "orig_" here handles orig_eax in the // x86 ptrace register sets, which otherwise have all fields // with reg_ prefixes. if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") { diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go index fec70a334..20376170d 100644 --- a/src/cmd/cgo/godefs.go +++ b/src/cmd/cgo/godefs.go @@ -180,7 +180,7 @@ func (p *Package) cdefs(f *File, srcfile string) string { for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") { - s := line[len("type ") : len(line)-len(" struct {")] + s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {") printf("typedef struct %s %s;\n", s, s) } } diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 60165961a..7adc795de 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -31,6 +31,7 @@ type Package struct { PackageName string // name of package PackagePath string PtrSize int64 + IntSize int64 GccOptions []string CgoFlags map[string]string // #cgo flags (CFLAGS, LDFLAGS) Written map[string]bool @@ -129,12 +130,19 @@ var ptrSizeMap = map[string]int64{ "arm": 4, } +var intSizeMap = map[string]int64{ + "386": 4, + "amd64": 8, + "arm": 4, +} + var cPrefix string var fset = token.NewFileSet() var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") var dynout = flag.String("dynout", "", "write -dynobj output to this file") +var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode") // These flags are for bootstrapping a new Go implementation, // to generate Go and C headers that match the data layout and @@ -144,8 +152,10 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C var objDir = flag.String("objdir", "", "object directory") var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") -var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo") +var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo") +var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo") var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code") +var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code") var goarch, goos string func main() { @@ -287,7 +297,11 @@ func newPackage(args []string) *Package { } ptrSize := ptrSizeMap[goarch] if ptrSize == 0 { - fatalf("unknown $GOARCH %q", goarch) + fatalf("unknown ptrSize for $GOARCH %q", goarch) + } + intSize := intSizeMap[goarch] + if intSize == 0 { + fatalf("unknown intSize for $GOARCH %q", goarch) } // Reset locale variables so gcc emits English errors [sic]. @@ -296,6 +310,7 @@ func newPackage(args []string) *Package { p := &Package{ PtrSize: ptrSize, + IntSize: intSize, GccOptions: gccOptions, CgoFlags: make(map[string]string), Written: make(map[string]bool), diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 5dfc16a02..a126cf17f 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,6 +14,7 @@ import ( "go/printer" "go/token" "os" + "sort" "strings" ) @@ -26,6 +27,8 @@ func (p *Package) writeDefs() { fc := creat(*objDir + "_cgo_defun.c") fm := creat(*objDir + "_cgo_main.c") + var gccgoInit bytes.Buffer + fflg := creat(*objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) @@ -50,19 +53,33 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + if *importSyscall { + fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + } if !*gccgo && *importRuntimeCgo { fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") } fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") - fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int) { *dst = syscall.Errno(x) }\n") + if *importSyscall { + fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n") + } - for name, def := range typedef { + typedefNames := make([]string, 0, len(typedef)) + for name := range typedef { + typedefNames = append(typedefNames, name) + } + sort.Strings(typedefNames) + for _, name := range typedefNames { + def := typedef[name] fmt.Fprintf(fgo2, "type %s ", name) conf.Fprint(fgo2, fset, def.Go) fmt.Fprintf(fgo2, "\n\n") } - fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + if *gccgo { + fmt.Fprintf(fgo2, "type _Ctype_void byte\n") + } else { + fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n") + } if *gccgo { fmt.Fprintf(fc, cPrologGccgo) @@ -70,6 +87,8 @@ func (p *Package) writeDefs() { fmt.Fprintf(fc, cProlog) } + gccgoSymbolPrefix := p.gccgoSymbolPrefix() + cVars := make(map[string]bool) for _, key := range nameKeys(p.Name) { n := p.Name[key] @@ -86,7 +105,12 @@ func (p *Package) writeDefs() { cVars[n.C] = true } - fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + if *gccgo { + fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, n.Mangle) + fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C) + } else { + fmt.Fprintf(fc, "void *·%s = &%s;\n", n.Mangle, n.C) + } fmt.Fprintf(fc, "\n") fmt.Fprintf(fgo2, "var %s ", n.Mangle) @@ -116,6 +140,14 @@ func (p *Package) writeDefs() { p.writeExports(fgo2, fc, fm) } + init := gccgoInit.String() + if init != "" { + fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor));") + fmt.Fprintln(fc, "static void init(void) {") + fmt.Fprint(fc, init) + fmt.Fprintln(fc, "}") + } + fgo2.Close() fc.Close() } @@ -131,6 +163,15 @@ func dynimport(obj string) { } if f, err := elf.Open(obj); err == nil { + if *dynlinker { + // Emit the cgo_dynamic_linker line. + if sec := f.Section(".interp"); sec != nil { + if data, err := sec.Data(); err == nil && len(data) > 1 { + // skip trailing \0 in data + fmt.Fprintf(stdout, "#pragma cgo_dynamic_linker %q\n", string(data[:len(data)-1])) + } + } + } sym, err := f.ImportedSymbols() if err != nil { fatalf("cannot load imported symbols from ELF file %s: %v", obj, err) @@ -138,16 +179,16 @@ func dynimport(obj string) { for _, s := range sym { targ := s.Name if s.Version != "" { - targ += "@" + s.Version + targ += "#" + s.Version } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s.Name, targ, s.Library) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library) } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from ELF file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -161,14 +202,14 @@ func dynimport(obj string) { if len(s) > 0 && s[0] == '_' { s = s[1:] } - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", s, s, "") + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", s, s, "") } lib, err := f.ImportedLibraries() if err != nil { fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err) } for _, l := range lib { - fmt.Fprintf(stdout, "#pragma dynimport _ _ %q\n", l) + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic _ _ %q\n", l) } return } @@ -180,7 +221,8 @@ func dynimport(obj string) { } for _, s := range sym { ss := strings.Split(s, ":") - fmt.Fprintf(stdout, "#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) + name := strings.Split(ss[0], "@")[0] + fmt.Fprintf(stdout, "#pragma cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1])) } return } @@ -247,6 +289,7 @@ func (p *Package) structType(n *Name) (string, int64) { func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { name := n.Go gtype := n.FuncType.Go + void := gtype.Results == nil || len(gtype.Results.List) == 0 if n.AddError { // Add "error" to return type list. // Type list is known to be 0 or 1 element - it's a C function. @@ -271,38 +314,58 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { if *gccgo { // Gccgo style hooks. - // we hook directly into C. gccgo goes not support cgocall yet. - if !n.AddError { - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") - } else { - // write a small wrapper to retrieve errno. - cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) - paramnames := []string(nil) - for i, param := range d.Type.Params.List { - paramName := fmt.Sprintf("p%d", i) - param.Names = []*ast.Ident{ast.NewIdent(paramName)} - paramnames = append(paramnames, paramName) + fmt.Fprint(fgo2, "\n") + cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) + paramnames := []string(nil) + for i, param := range d.Type.Params.List { + paramName := fmt.Sprintf("p%d", i) + param.Names = []*ast.Ident{ast.NewIdent(paramName)} + paramnames = append(paramnames, paramName) + } + + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n") + fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n") + if n.AddError { + fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n") + } + fmt.Fprint(fgo2, "\t") + if !void { + fmt.Fprint(fgo2, "r := ") + } + fmt.Fprintf(fgo2, "%s(%s)\n", cname, strings.Join(paramnames, ", ")) + + if n.AddError { + fmt.Fprint(fgo2, "\te := syscall.GetErrno()\n") + fmt.Fprint(fgo2, "\tif e != 0 {\n") + fmt.Fprint(fgo2, "\t\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") } - conf.Fprint(fgo2, fset, d) - fmt.Fprintf(fgo2, "{\n") - fmt.Fprintf(fgo2, "\tsyscall.SetErrno(0)\n") - fmt.Fprintf(fgo2, "\tr := %s(%s)\n", cname, strings.Join(paramnames, ", ")) - fmt.Fprintf(fgo2, "\te := syscall.GetErrno()\n") - fmt.Fprintf(fgo2, "\tif e != 0 {\n") - fmt.Fprintf(fgo2, "\t\treturn r, e\n") - fmt.Fprintf(fgo2, "\t}\n") - fmt.Fprintf(fgo2, "\treturn r, nil\n") - fmt.Fprintf(fgo2, "}\n") - // declare the C function. - fmt.Fprintf(fgo2, "//extern %s\n", n.C) - d.Name = ast.NewIdent(cname) + fmt.Fprint(fgo2, "e\n") + fmt.Fprint(fgo2, "\t}\n") + fmt.Fprint(fgo2, "\treturn ") + if !void { + fmt.Fprint(fgo2, "r, ") + } + fmt.Fprint(fgo2, "nil\n") + } else if !void { + fmt.Fprint(fgo2, "\treturn r\n") + } + + fmt.Fprint(fgo2, "}\n") + + // declare the C function. + fmt.Fprintf(fgo2, "//extern %s\n", n.C) + d.Name = ast.NewIdent(cname) + if n.AddError { l := d.Type.Results.List d.Type.Results.List = l[:len(l)-1] - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") } + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, "\n") + return } conf.Fprint(fgo2, fset, d) @@ -317,6 +380,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { _, argSize = p.structType(n) // C wrapper calls into gcc, passing a pointer to the argument frame. + fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) fmt.Fprintf(fc, "\n") fmt.Fprintf(fc, "void\n") @@ -456,11 +520,13 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcc, "\nextern void crosscall2(void (*fn)(void *, int), void *, int);\n\n") + for _, exp := range p.ExpFunc { fn := exp.Func @@ -552,7 +618,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { s += ")" fmt.Fprintf(fgcch, "\nextern %s;\n", s) - fmt.Fprintf(fgcc, "extern _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) + fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *, int);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\n%s\n", s) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\t%s __attribute__((packed)) a;\n", ctype) @@ -585,7 +651,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { if fn.Recv != nil { goname = "_cgoexpwrap" + cPrefix + "_" + fn.Recv.List[0].Names[0].Name + "_" + goname } - fmt.Fprintf(fc, "#pragma dynexport %s %s\n", goname, goname) + fmt.Fprintf(fc, "#pragma cgo_export %s\n", goname) fmt.Fprintf(fc, "extern void ·%s();\n\n", goname) fmt.Fprintf(fc, "#pragma textflag 7\n") // no split stack, so no use of m or g fmt.Fprintf(fc, "void\n") @@ -641,32 +707,22 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fgcc := creat(*objDir + "_cgo_export.c") fgcch := creat(*objDir + "_cgo_export.h") - _ = fgcc + + gccgoSymbolPrefix := p.gccgoSymbolPrefix() fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) - fmt.Fprintf(fgcch, "%s\n", gccExportHeaderProlog) - fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") + fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) - clean := func(r rune) rune { - switch { - case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', - '0' <= r && r <= '9': - return r - } - return '_' - } - gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix) + fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n") + + fmt.Fprintf(fm, "#include \"_cgo_export.h\"\n") for _, exp := range p.ExpFunc { - // TODO: support functions with receivers. fn := exp.Func fntype := fn.Type - if !ast.IsExported(fn.Name.Name) { - fatalf("cannot export unexported function %s with gccgo", fn.Name) - } - cdeclBuf := new(bytes.Buffer) resultCount := 0 forFieldList(fntype.Results, @@ -692,28 +748,135 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) { fmt.Fprintf(cdeclBuf, "struct %s_result", exp.ExpName) } - // The function name. - fmt.Fprintf(cdeclBuf, " "+exp.ExpName) - gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name) - fmt.Fprintf(cdeclBuf, " (") + cRet := cdeclBuf.String() + + cdeclBuf = new(bytes.Buffer) + fmt.Fprintf(cdeclBuf, "(") + if fn.Recv != nil { + fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String()) + } // Function parameters. forFieldList(fntype.Params, func(i int, atype ast.Expr) { - if i > 0 { + if i > 0 || fn.Recv != nil { fmt.Fprintf(cdeclBuf, ", ") } t := p.cgoType(atype) fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i) }) fmt.Fprintf(cdeclBuf, ")") - cdecl := cdeclBuf.String() + cParams := cdeclBuf.String() + + goName := "Cgoexp_" + exp.ExpName + fmt.Fprintf(fgcch, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, goName) + fmt.Fprint(fgcch, "\n") + + fmt.Fprint(fgcc, "\n") + fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams) + fmt.Fprint(fgcc, "\t") + if resultCount > 0 { + fmt.Fprint(fgcc, "return ") + } + fmt.Fprintf(fgcc, "%s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgcc, "recv") + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgcc, ", ") + } + fmt.Fprintf(fgcc, "p%d", i) + }) + fmt.Fprint(fgcc, ");\n") + fmt.Fprint(fgcc, "}\n") - fmt.Fprintf(fgcch, "extern %s __asm__(\"%s\");\n", cdecl, gccgoSymbol) // Dummy declaration for _cgo_main.c - fmt.Fprintf(fm, "%s {}\n", cdecl) + fmt.Fprintf(fm, "%s %s %s {}\n", cRet, goName, cParams) + + // For gccgo we use a wrapper function in Go, in order + // to call CgocallBack and CgocallBackDone. + + // This code uses printer.Fprint, not conf.Fprint, + // because we don't want //line comments in the middle + // of the function types. + fmt.Fprint(fgo2, "\n") + fmt.Fprintf(fgo2, "func %s(", goName) + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv ") + printer.Fprint(fgo2, fset, fn.Recv.List[0].Type) + } + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 || fn.Recv != nil { + fmt.Fprintf(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d ", i) + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprintf(fgo2, ")") + if resultCount > 0 { + fmt.Fprintf(fgo2, " (") + forFieldList(fntype.Results, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + printer.Fprint(fgo2, fset, atype) + }) + fmt.Fprint(fgo2, ")") + } + fmt.Fprint(fgo2, " {\n") + fmt.Fprint(fgo2, "\tsyscall.CgocallBack()\n") + fmt.Fprint(fgo2, "\tdefer syscall.CgocallBackDone()\n") + fmt.Fprint(fgo2, "\t") + if resultCount > 0 { + fmt.Fprint(fgo2, "return ") + } + if fn.Recv != nil { + fmt.Fprint(fgo2, "recv.") + } + fmt.Fprintf(fgo2, "%s(", exp.Func.Name) + forFieldList(fntype.Params, + func(i int, atype ast.Expr) { + if i > 0 { + fmt.Fprint(fgo2, ", ") + } + fmt.Fprintf(fgo2, "p%d", i) + }) + fmt.Fprint(fgo2, ")\n") + fmt.Fprint(fgo2, "}\n") } } +// Return the package prefix when using gccgo. +func (p *Package) gccgoSymbolPrefix() string { + if !*gccgo { + return "" + } + + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + + if *gccgopkgpath != "" { + return strings.Map(clean, *gccgopkgpath) + } + if *gccgoprefix == "" && p.PackageName == "main" { + return "main" + } + prefix := strings.Map(clean, *gccgoprefix) + if prefix == "" { + prefix = "go" + } + return prefix + "." + p.PackageName +} + // Call a function for each entry in an ast.FieldList, passing the // index into the list and the type. func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { @@ -742,8 +905,8 @@ func c(repr string, args ...interface{}) *TypeRepr { var goTypes = map[string]*Type{ "bool": {Size: 1, Align: 1, C: c("GoUint8")}, "byte": {Size: 1, Align: 1, C: c("GoUint8")}, - "int": {Size: 4, Align: 4, C: c("GoInt")}, - "uint": {Size: 4, Align: 4, C: c("GoUint")}, + "int": {Size: 0, Align: 0, C: c("GoInt")}, + "uint": {Size: 0, Align: 0, C: c("GoUint")}, "rune": {Size: 4, Align: 4, C: c("GoInt32")}, "int8": {Size: 1, Align: 1, C: c("GoInt8")}, "uint8": {Size: 1, Align: 1, C: c("GoUint8")}, @@ -804,12 +967,21 @@ func (p *Package) cgoType(e ast.Expr) *Type { return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")} } if t.Name == "string" { - return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")} + // The string data is 1 pointer + 1 int, but this always + // rounds to 2 pointers due to alignment. + return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")} } if t.Name == "error" { 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 } @@ -898,18 +1070,21 @@ const cPrologGccgo = ` #include <stdint.h> #include <string.h> +typedef unsigned char byte; +typedef intptr_t intgo; + struct __go_string { const unsigned char *__data; - int __length; + intgo __length; }; typedef struct __go_open_array { void* __values; - int __count; - int __capacity; + intgo __count; + intgo __capacity; } Slice; -struct __go_string __go_byte_array_to_string(const void* p, int len); +struct __go_string __go_byte_array_to_string(const void* p, intgo len); struct __go_open_array __go_string_to_byte_array (struct __go_string str); const char *CString(struct __go_string s) { @@ -917,23 +1092,25 @@ const char *CString(struct __go_string s) { } struct __go_string GoString(char *p) { - int len = (p != NULL) ? strlen(p) : 0; + intgo len = (p != NULL) ? strlen(p) : 0; return __go_byte_array_to_string(p, len); } -struct __go_string GoStringN(char *p, int n) { +struct __go_string GoStringN(char *p, intgo n) { return __go_byte_array_to_string(p, n); } -Slice GoBytes(char *p, int n) { +Slice GoBytes(char *p, intgo n) { struct __go_string s = { (const unsigned char *)p, n }; return __go_string_to_byte_array(s); } ` +func (p *Package) gccExportHeaderProlog() string { + return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1) +} + const gccExportHeaderProlog = ` -typedef int GoInt; -typedef unsigned int GoUint; typedef signed char GoInt8; typedef unsigned char GoUint8; typedef short GoInt16; @@ -942,6 +1119,8 @@ typedef int GoInt32; typedef unsigned int GoUint32; typedef long long GoInt64; typedef unsigned long long GoUint64; +typedef GoIntGOINTBITS GoInt; +typedef GoUintGOINTBITS GoUint; typedef __SIZE_TYPE__ GoUintptr; typedef float GoFloat32; typedef double GoFloat64; @@ -952,4 +1131,5 @@ typedef struct { char *p; int n; } GoString; typedef void *GoMap; typedef void *GoChan; typedef struct { void *t; void *v; } GoInterface; +typedef struct { void *data; int len; int cap; } GoSlice; ` diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index a0f216614..4e7800d12 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -5,9 +5,9 @@ package main import ( + "bytes" "fmt" "go/token" - "io/ioutil" "os" "os/exec" ) @@ -16,50 +16,17 @@ import ( // It returns the output to standard output and standard error. // ok indicates whether the command exited successfully. func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) { - cmd, err := exec.LookPath(argv[0]) - if err != nil { - fatalf("exec %s: %s", argv[0], err) - } - r0, w0, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r1, w1, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - r2, w2, err := os.Pipe() - if err != nil { - fatalf("%s", err) - } - p, err := os.StartProcess(cmd, argv, &os.ProcAttr{Files: []*os.File{r0, w1, w2}}) - if err != nil { - fatalf("%s", err) - } - r0.Close() - w1.Close() - w2.Close() - c := make(chan bool) - go func() { - w0.Write(stdin) - w0.Close() - c <- true - }() - go func() { - stdout, _ = ioutil.ReadAll(r1) - r1.Close() - c <- true - }() - stderr, _ = ioutil.ReadAll(r2) - r2.Close() - <-c - <-c - - state, err := p.Wait() - if err != nil { + p := exec.Command(argv[0], argv[1:]...) + p.Stdin = bytes.NewReader(stdin) + var bout, berr bytes.Buffer + p.Stdout = &bout + p.Stderr = &berr + err := p.Run() + if _, ok := err.(*exec.ExitError); err != nil && !ok { fatalf("%s", err) } - ok = state.Success() + ok = p.ProcessState.Success() + stdout, stderr = bout.Bytes(), berr.Bytes() return } diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile new file mode 100644 index 000000000..3f528d751 --- /dev/null +++ b/src/cmd/cov/Makefile @@ -0,0 +1,5 @@ +# 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. + +include ../../Make.dist diff --git a/src/cmd/cov/doc.go b/src/cmd/cov/doc.go new file mode 100644 index 000000000..ab5d1220a --- /dev/null +++ b/src/cmd/cov/doc.go @@ -0,0 +1,36 @@ +// 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. + +// +build ignore + +/* + +Cov is a rudimentary code coverage tool. + +Usage: + go tool cov [-lsv] [-g substring] [-m minlines] [6.out args] + +Given a command to run, it runs the command while tracking which +sections of code have been executed. When the command finishes, +cov prints the line numbers of sections of code in the binary that +were not executed. With no arguments it assumes the command "6.out". + + +The options are: + + -l + print full path names instead of paths relative to the current directory + -s + show the source code that didn't execute, in addition to the line numbers. + -v + print debugging information during the run. + -g substring + restrict the coverage analysis to functions or files whose names contain substring + -m minlines + only report uncovered sections of code larger than minlines lines + +The program is the same for all architectures: 386, amd64, and arm. + +*/ +package main diff --git a/src/cmd/cov/main.c b/src/cmd/cov/main.c new file mode 100644 index 000000000..33ef49e17 --- /dev/null +++ b/src/cmd/cov/main.c @@ -0,0 +1,484 @@ +// 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. + +/* + * code coverage + */ + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include "tree.h" + +#include <ureg_amd64.h> +#include <mach.h> +typedef struct Ureg Ureg; + +void +usage(void) +{ + fprint(2, "usage: cov [-lsv] [-g substring] [-m minlines] [6.out args...]\n"); + fprint(2, "-g specifies pattern of interesting functions or files\n"); + exits("usage"); +} + +typedef struct Range Range; +struct Range +{ + uvlong pc; + uvlong epc; +}; + +int chatty; +int fd; +int longnames; +int pid; +int doshowsrc; +Map *mem; +Map *text; +Fhdr fhdr; +char *substring; +char cwd[1000]; +int ncwd; +int minlines = -1000; + +Tree breakpoints; // code ranges not run + +/* + * comparison for Range structures + * they are "equal" if they overlap, so + * that a search for [pc, pc+1) finds the + * Range containing pc. + */ +int +rangecmp(void *va, void *vb) +{ + Range *a = va, *b = vb; + if(a->epc <= b->pc) + return 1; + if(b->epc <= a->pc) + return -1; + return 0; +} + +/* + * remember that we ran the section of code [pc, epc). + */ +void +ran(uvlong pc, uvlong epc) +{ + Range key; + Range *r; + uvlong oldepc; + + if(chatty) + print("run %#llux-%#llux\n", pc, epc); + + key.pc = pc; + key.epc = pc+1; + r = treeget(&breakpoints, &key); + if(r == nil) + sysfatal("unchecked breakpoint at %#llux+%d", pc, (int)(epc-pc)); + + // Might be that the tail of the sequence + // was run already, so r->epc is before the end. + // Adjust len. + if(epc > r->epc) + epc = r->epc; + + if(r->pc == pc) { + r->pc = epc; + } else { + // Chop r to before pc; + // add new entry for after if needed. + // Changing r->epc does not affect r's position in the tree. + oldepc = r->epc; + r->epc = pc; + if(epc < oldepc) { + Range *n; + n = malloc(sizeof *n); + if(n == nil) + sysfatal("out of memory"); + n->pc = epc; + n->epc = oldepc; + treeput(&breakpoints, n, n); + } + } +} + +void +showsrc(char *file, int line1, int line2) +{ + Biobuf *b; + char *p; + int n, stop; + + if((b = Bopen(file, OREAD)) == nil) { + print("\topen %s: %r\n", file); + return; + } + + for(n=1; n<line1 && (p = Brdstr(b, '\n', 1)) != nil; n++) + free(p); + + // print up to five lines (this one and 4 more). + // if there are more than five lines, print 4 and "..." + stop = n+4; + if(stop > line2) + stop = line2; + if(stop < line2) + stop--; + for(; n<=stop && (p = Brdstr(b, '\n', 1)) != nil; n++) { + print(" %d %s\n", n, p); + free(p); + } + if(n < line2) + print(" ...\n"); + Bterm(b); +} + +/* + * if s is in the current directory or below, + * return the relative path. + */ +char* +shortname(char *s) +{ + if(!longnames && strlen(s) > ncwd && memcmp(s, cwd, ncwd) == 0 && s[ncwd] == '/') + return s+ncwd+1; + return s; +} + +/* + * we've decided that [pc, epc) did not run. + * do something about it. + */ +void +missing(uvlong pc, uvlong epc) +{ + char file[1000]; + int line1, line2; + char buf[100]; + Symbol s; + char *p; + uvlong uv; + + if(!findsym(pc, CTEXT, &s) || !fileline(file, sizeof file, pc)) { + notfound: + print("%#llux-%#llux\n", pc, epc); + return; + } + p = strrchr(file, ':'); + *p++ = 0; + line1 = atoi(p); + for(uv=pc; uv<epc; ) { + if(!fileline(file, sizeof file, epc-2)) + goto notfound; + uv += machdata->instsize(text, uv); + } + p = strrchr(file, ':'); + *p++ = 0; + line2 = atoi(p); + + if(line2+1-line2 < minlines) + return; + + if(pc == s.value) { + // never entered function + print("%s:%d %s never called (%#llux-%#llux)\n", shortname(file), line1, s.name, pc, epc); + return; + } + if(pc <= s.value+13) { + // probably stub for stack growth. + // check whether last instruction is call to morestack. + // the -5 below is the length of + // CALL sys.morestack. + buf[0] = 0; + machdata->das(text, epc-5, 0, buf, sizeof buf); + if(strstr(buf, "morestack")) + return; + } + + if(epc - pc == 5) { + // check for CALL sys.panicindex + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strstr(buf, "panicindex")) + return; + } + + if(epc - pc == 2 || epc -pc == 3) { + // check for XORL inside shift. + // (on x86 have to implement large left or unsigned right shift with explicit zeroing). + // f+90 0x00002c9f CMPL CX,$20 + // f+93 0x00002ca2 JCS f+97(SB) + // f+95 0x00002ca4 XORL AX,AX <<< + // f+97 0x00002ca6 SHLL CL,AX + // f+99 0x00002ca8 MOVL $1,CX + // + // f+c8 0x00002cd7 CMPL CX,$40 + // f+cb 0x00002cda JCS f+d0(SB) + // f+cd 0x00002cdc XORQ AX,AX <<< + // f+d0 0x00002cdf SHLQ CL,AX + // f+d3 0x00002ce2 MOVQ $1,CX + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strncmp(buf, "XOR", 3) == 0) { + machdata->das(text, epc, 0, buf, sizeof buf); + if(strncmp(buf, "SHL", 3) == 0 || strncmp(buf, "SHR", 3) == 0) + return; + } + } + + if(epc - pc == 3) { + // check for SAR inside shift. + // (on x86 have to implement large signed right shift as >>31). + // f+36 0x00016216 CMPL CX,$20 + // f+39 0x00016219 JCS f+3e(SB) + // f+3b 0x0001621b SARL $1f,AX <<< + // f+3e 0x0001621e SARL CL,AX + // f+40 0x00016220 XORL CX,CX + // f+42 0x00016222 CMPL CX,AX + buf[0] = 0; + machdata->das(text, pc, 0, buf, sizeof buf); + if(strncmp(buf, "SAR", 3) == 0) { + machdata->das(text, epc, 0, buf, sizeof buf); + if(strncmp(buf, "SAR", 3) == 0) + return; + } + } + + // show first instruction to make clear where we were. + machdata->das(text, pc, 0, buf, sizeof buf); + + if(line1 != line2) + print("%s:%d,%d %#llux-%#llux %s\n", + shortname(file), line1, line2, pc, epc, buf); + else + print("%s:%d %#llux-%#llux %s\n", + shortname(file), line1, pc, epc, buf); + if(doshowsrc) + showsrc(file, line1, line2); +} + +/* + * walk the tree, calling missing for each non-empty + * section of missing code. + */ +void +walktree(TreeNode *t) +{ + Range *n; + + if(t == nil) + return; + walktree(t->left); + n = t->key; + if(n->pc < n->epc) + missing(n->pc, n->epc); + walktree(t->right); +} + +/* + * set a breakpoint all over [pc, epc) + * and remember that we did. + */ +void +breakpoint(uvlong pc, uvlong epc) +{ + Range *r; + + r = malloc(sizeof *r); + if(r == nil) + sysfatal("out of memory"); + r->pc = pc; + r->epc = epc; + treeput(&breakpoints, r, r); + + for(; pc < epc; pc+=machdata->bpsize) + put1(mem, pc, machdata->bpinst, machdata->bpsize); +} + +/* + * install breakpoints over all text symbols + * that match the pattern. + */ +void +cover(void) +{ + Symbol s; + char *lastfn; + uvlong lastpc; + int i; + char buf[200]; + + lastfn = nil; + lastpc = 0; + for(i=0; textsym(&s, i); i++) { + switch(s.type) { + case 'T': + case 't': + if(lastpc != 0) { + breakpoint(lastpc, s.value); + lastpc = 0; + } + // Ignore second entry for a given name; + // that's the debugging blob. + if(lastfn && strcmp(s.name, lastfn) == 0) + break; + lastfn = s.name; + buf[0] = 0; + fileline(buf, sizeof buf, s.value); + if(substring == nil || strstr(buf, substring) || strstr(s.name, substring)) + lastpc = s.value; + } + } +} + +uvlong +rgetzero(Map *map, char *reg) +{ + USED(map); + USED(reg); + + return 0; +} + +/* + * remove the breakpoints at pc and successive instructions, + * up to and including the first jump or other control flow transfer. + */ +void +uncover(uvlong pc) +{ + uchar buf[1000]; + int n, n1, n2; + uvlong foll[2]; + + // Double-check that we stopped at a breakpoint. + if(get1(mem, pc, buf, machdata->bpsize) < 0) + sysfatal("read mem inst at %#llux: %r", pc); + if(memcmp(buf, machdata->bpinst, machdata->bpsize) != 0) + sysfatal("stopped at %#llux; not at breakpoint %d", pc, machdata->bpsize); + + // Figure out how many bytes of straight-line code + // there are in the text starting at pc. + n = 0; + while(n < sizeof buf) { + n1 = machdata->instsize(text, pc+n); + if(n+n1 > sizeof buf) + break; + n2 = machdata->foll(text, pc+n, rgetzero, foll); + n += n1; + if(n2 != 1 || foll[0] != pc+n) + break; + } + + // Record that this section of code ran. + ran(pc, pc+n); + + // Put original instructions back. + if(get1(text, pc, buf, n) < 0) + sysfatal("get1: %r"); + if(put1(mem, pc, buf, n) < 0) + sysfatal("put1: %r"); +} + +int +startprocess(char **argv) +{ + int pid; + + if((pid = fork()) < 0) + sysfatal("fork: %r"); + if(pid == 0) { + pid = getpid(); + if(ctlproc(pid, "hang") < 0) + sysfatal("ctlproc hang: %r"); + exec(argv[0], argv); + sysfatal("exec %s: %r", argv[0]); + } + if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) + sysfatal("attach %d %s: %r", pid, argv[0]); + return pid; +} + +int +go(void) +{ + uvlong pc; + char buf[100]; + int n; + + for(n = 0;; n++) { + ctlproc(pid, "startstop"); + if(get8(mem, offsetof(Ureg, ip), &pc) < 0) { + rerrstr(buf, sizeof buf); + if(strstr(buf, "exited") || strstr(buf, "No such process")) + return n; + sysfatal("cannot read pc: %r"); + } + pc--; + if(put8(mem, offsetof(Ureg, ip), pc) < 0) + sysfatal("cannot write pc: %r"); + uncover(pc); + } +} + +void +main(int argc, char **argv) +{ + int n; + + ARGBEGIN{ + case 'g': + substring = EARGF(usage()); + break; + case 'l': + longnames++; + break; + case 'n': + minlines = atoi(EARGF(usage())); + break; + case 's': + doshowsrc = 1; + break; + case 'v': + chatty++; + break; + default: + usage(); + }ARGEND + + getwd(cwd, sizeof cwd); + ncwd = strlen(cwd); + + if(argc == 0) { + *--argv = "6.out"; + } + fd = open(argv[0], OREAD); + if(fd < 0) + sysfatal("open %s: %r", argv[0]); + if(crackhdr(fd, &fhdr) <= 0) + sysfatal("crackhdr: %r"); + machbytype(fhdr.type); + if(syminit(fd, &fhdr) <= 0) + sysfatal("syminit: %r"); + text = loadmap(nil, fd, &fhdr); + if(text == nil) + sysfatal("loadmap: %r"); + pid = startprocess(argv); + mem = attachproc(pid, &fhdr); + if(mem == nil) + sysfatal("attachproc: %r"); + breakpoints.cmp = rangecmp; + cover(); + n = go(); + walktree(breakpoints.root); + if(chatty) + print("%d breakpoints\n", n); + detachproc(mem); + exits(0); +} + diff --git a/src/cmd/cov/tree.c b/src/cmd/cov/tree.c new file mode 100644 index 000000000..366a47efd --- /dev/null +++ b/src/cmd/cov/tree.c @@ -0,0 +1,245 @@ +// Renamed from Map to Tree to avoid conflict with libmach. + +/* +Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements, + Massachusetts Institute of Technology +Portions Copyright (c) 2009 The Go Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +// Mutable map structure, but still based on +// Okasaki, Red Black Trees in a Functional Setting, JFP 1999, +// which is a lot easier than the traditional red-black +// and plenty fast enough for me. (Also I could copy +// and edit fmap.c.) + +#include <u.h> +#include <libc.h> +#include "tree.h" + +enum +{ + Red = 0, + Black = 1 +}; + + +// Red-black trees are binary trees with this property: +// 1. No red node has a red parent. +// 2. Every path from the root to a leaf contains the +// same number of black nodes. + +static TreeNode* +rwTreeNode(TreeNode *p, int color, TreeNode *left, void *key, void *value, TreeNode *right) +{ + if(p == nil) + p = malloc(sizeof *p); + if(p == nil) + sysfatal("out of memory"); + p->color = color; + p->left = left; + p->key = key; + p->value = value; + p->right = right; + return p; +} + +static TreeNode* +balance(TreeNode *m0) +{ + void *xk, *xv, *yk, *yv, *zk, *zv; + TreeNode *a, *b, *c, *d; + TreeNode *m1, *m2; + int color; + TreeNode *left, *right; + void *key, *value; + + color = m0->color; + left = m0->left; + key = m0->key; + value = m0->value; + right = m0->right; + + // Okasaki notation: (T is mkTreeNode, B is Black, R is Red, x, y, z are key-value. + // + // balance B (T R (T R a x b) y c) z d + // balance B (T R a x (T R b y c)) z d + // balance B a x (T R (T R b y c) z d) + // balance B a x (T R b y (T R c z d)) + // + // = T R (T B a x b) y (T B c z d) + + if(color == Black){ + if(left && left->color == Red){ + if(left->left && left->left->color == Red){ + a = left->left->left; + xk = left->left->key; + xv = left->left->value; + b = left->left->right; + yk = left->key; + yv = left->value; + c = left->right; + zk = key; + zv = value; + d = right; + m1 = left; + m2 = left->left; + goto hard; + }else if(left->right && left->right->color == Red){ + a = left->left; + xk = left->key; + xv = left->value; + b = left->right->left; + yk = left->right->key; + yv = left->right->value; + c = left->right->right; + zk = key; + zv = value; + d = right; + m1 = left; + m2 = left->right; + goto hard; + } + }else if(right && right->color == Red){ + if(right->left && right->left->color == Red){ + a = left; + xk = key; + xv = value; + b = right->left->left; + yk = right->left->key; + yv = right->left->value; + c = right->left->right; + zk = right->key; + zv = right->value; + d = right->right; + m1 = right; + m2 = right->left; + goto hard; + }else if(right->right && right->right->color == Red){ + a = left; + xk = key; + xv = value; + b = right->left; + yk = right->key; + yv = right->value; + c = right->right->left; + zk = right->right->key; + zv = right->right->value; + d = right->right->right; + m1 = right; + m2 = right->right; + goto hard; + } + } + } + return rwTreeNode(m0, color, left, key, value, right); + +hard: + return rwTreeNode(m0, Red, rwTreeNode(m1, Black, a, xk, xv, b), + yk, yv, rwTreeNode(m2, Black, c, zk, zv, d)); +} + +static TreeNode* +ins0(TreeNode *p, void *k, void *v, TreeNode *rw) +{ + if(p == nil) + return rwTreeNode(rw, Red, nil, k, v, nil); + if(p->key == k){ + if(rw) + return rwTreeNode(rw, p->color, p->left, k, v, p->right); + p->value = v; + return p; + } + if(p->key < k) + p->left = ins0(p->left, k, v, rw); + else + p->right = ins0(p->right, k, v, rw); + return balance(p); +} + +static TreeNode* +ins1(Tree *m, TreeNode *p, void *k, void *v, TreeNode *rw) +{ + int i; + + if(p == nil) + return rwTreeNode(rw, Red, nil, k, v, nil); + i = m->cmp(p->key, k); + if(i == 0){ + if(rw) + return rwTreeNode(rw, p->color, p->left, k, v, p->right); + p->value = v; + return p; + } + if(i < 0) + p->left = ins1(m, p->left, k, v, rw); + else + p->right = ins1(m, p->right, k, v, rw); + return balance(p); +} + +void +treeputelem(Tree *m, void *key, void *val, TreeNode *rw) +{ + if(m->cmp) + m->root = ins1(m, m->root, key, val, rw); + else + m->root = ins0(m->root, key, val, rw); +} + +void +treeput(Tree *m, void *key, void *val) +{ + treeputelem(m, key, val, nil); +} + +void* +treeget(Tree *m, void *key) +{ + int i; + TreeNode *p; + + p = m->root; + if(m->cmp){ + for(;;){ + if(p == nil) + return nil; + i = m->cmp(p->key, key); + if(i < 0) + p = p->left; + else if(i > 0) + p = p->right; + else + return p->value; + } + }else{ + for(;;){ + if(p == nil) + return nil; + if(p->key == key) + return p->value; + if(p->key < key) + p = p->left; + else + p = p->right; + } + } +} diff --git a/src/cmd/cov/tree.h b/src/cmd/cov/tree.h new file mode 100644 index 000000000..a716d83ad --- /dev/null +++ b/src/cmd/cov/tree.h @@ -0,0 +1,47 @@ +// Renamed from Map to Tree to avoid conflict with libmach. + +/* +Copyright (c) 2003-2007 Russ Cox, Tom Bergan, Austin Clements, + Massachusetts Institute of Technology +Portions Copyright (c) 2009 The Go Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +typedef struct Tree Tree; +typedef struct TreeNode TreeNode; +struct Tree +{ + int (*cmp)(void*, void*); + TreeNode *root; +}; + +struct TreeNode +{ + int color; + TreeNode *left; + void *key; + void *value; + TreeNode *right; +}; + +void *treeget(Tree*, void*); +void treeput(Tree*, void*, void*); +void treeputelem(Tree*, void*, void*, TreeNode*); diff --git a/src/cmd/dist/README b/src/cmd/dist/README index cf194faf0..e6d08cf02 100644 --- a/src/cmd/dist/README +++ b/src/cmd/dist/README @@ -31,8 +31,8 @@ Bufs or Vecs on the stack should be ... main code ... bprintf(&b1, "hello, world"); vadd(&v1, bstr(&b1)); // v1 takes a copy of its argument - bprintf(&b1, "another string"); - vadd(&v1, bstr(&b1)); // v1 now has two strings + bprintf(&b2, "another string"); + vadd(&v1, bstr(&b2)); // v1 now has two strings bfree(&b1); bfree(&b2); diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h index c19b1f468..fcd81cc51 100644 --- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -10,7 +10,9 @@ typedef long long Time; #define nil ((void*)0) #define nelem(x) (sizeof(x)/sizeof((x)[0])) +#ifndef PLAN9 #define USED(x) ((void)(x)) +#endif // A Buf is a byte buffer, like Go's []byte. typedef struct Buf Buf; @@ -106,6 +108,7 @@ void xmain(int argc, char **argv); // portability layer (plan9.c, unix.c, windows.c) bool contains(char *p, char *sep); +void errprintf(char*, ...); void fatal(char *msg, ...); bool hasprefix(char *p, char *prefix); bool hassuffix(char *p, char *suffix); @@ -120,6 +123,7 @@ void runv(Buf *b, char *dir, int mode, Vec *argv); void bgrunv(char *dir, int mode, Vec *argv); void bgwait(void); bool streq(char*, char*); +bool cansse2(void); void writefile(Buf*, char*, int); void xatexit(void (*f)(void)); void xexit(int); @@ -147,3 +151,6 @@ int xstrlen(char*); char* xstrrchr(char*, int); char* xstrstr(char*, char*); char* xworkdir(void); +int xsamefile(char*, char*); +char* xgetgoarm(void); +int xtryexecfunc(void (*)(void)); diff --git a/src/cmd/dist/arg.h b/src/cmd/dist/arg.h index 6eef0353b..9819765b1 100644 --- a/src/cmd/dist/arg.h +++ b/src/cmd/dist/arg.h @@ -28,7 +28,7 @@ THE SOFTWARE. /* command line */ extern char *argv0; -#define ARGBEGIN for((argv0?0:(argv0=(*argv))),argv++,argc--;\ +#define ARGBEGIN for((argv0=(argv0?argv0:*argv)),argv++,argc--;\ argv[0] && argv[0][0]=='-' && argv[0][1];\ argc--, argv++) {\ char *_args, *_argt;\ @@ -37,7 +37,6 @@ extern char *argv0; if(_args[0]=='-' && _args[1]==0){\ argc--; argv++; break;\ }\ - _argc = 0;\ while((_argc = *_args++) != 0)\ switch(_argc) #define ARGEND _argt=0;USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc); diff --git a/src/cmd/dist/arm.c b/src/cmd/dist/arm.c new file mode 100644 index 000000000..dafc5c1c2 --- /dev/null +++ b/src/cmd/dist/arm.c @@ -0,0 +1,79 @@ +// 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. + +#include "a.h" + +#ifndef __ARMEL__ +char * +xgetgoarm(void) +{ + return "6"; +} +#else +static void useVFPv3(void); +static void useVFPv1(void); + +char * +xgetgoarm(void) +{ +#if defined(__NetBSD__) || defined(__FreeBSD__) + // NetBSD has buggy support for VFPv2 (incorrect inexact, + // denormial, and NaN handling). When GOARM=6, some of our + // math tests fails on Raspberry Pi. + // Thus we return "5" here for safety, the user is free + // to override. + // Note: using GOARM=6 with cgo can trigger a kernel assertion + // failure and crash NetBSD/evbarm kernel. + // FreeBSD also have broken VFP support, so disable VFP also + // on FreeBSD. + return "5"; +#endif + if(xtryexecfunc(useVFPv3)) + return "7"; + else if(xtryexecfunc(useVFPv1)) + return "6"; + return "5"; +} + +static void +useVFPv3(void) +{ + // try to run VFPv3-only "vmov.f64 d0, #112" instruction + // we can't use that instruction directly, because we + // might be compiling with a soft-float only toolchain. + // + // some newer toolchains are configured to use thumb + // by default, so we need to do some mode changing magic + // here. + // We can use "bx pc; nop" here, but GNU as(1) insists + // on warning us + // "use of r15 in bx in ARM mode is not really useful" + // so we workaround that by using "bx r0" + __asm__ __volatile__ ("mov r0, pc"); + __asm__ __volatile__ ("bx r0"); + __asm__ __volatile__ (".word 0xeeb70b00"); // vmov.f64 d0, #112 + __asm__ __volatile__ (".word 0xe12fff1e"); // bx lr +} + +static void +useVFPv1(void) +{ + // try to run "vmov.f64 d0, d0" instruction + // we can't use that instruction directly, because we + // might be compiling with a soft-float only toolchain + // + // some newer toolchains are configured to use thumb + // by default, so we need to do some mode changing magic + // here. + // We can use "bx pc; nop" here, but GNU as(1) insists + // on warning us + // "use of r15 in bx in ARM mode is not really useful" + // so we workaround that by using "bx r0" + __asm__ __volatile__ ("mov r0, pc"); + __asm__ __volatile__ ("bx r0"); + __asm__ __volatile__ (".word 0xeeb00b40"); // vomv.f64 d0, d0 + __asm__ __volatile__ (".word 0xe12fff1e"); // bx lr +} + +#endif diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 7f9aa7bcd..dda45ca62 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -16,6 +16,8 @@ char *gohostarch; char *gohostchar; char *gohostos; char *goos; +char *goarm; +char *go386; char *goroot = GOROOT_FINAL; char *goroot_final = GOROOT_FINAL; char *workdir; @@ -96,6 +98,20 @@ init(void) if(find(goos, okgoos, nelem(okgoos)) < 0) fatal("unknown $GOOS %s", goos); + xgetenv(&b, "GOARM"); + if(b.len == 0) + bwritestr(&b, xgetgoarm()); + goarm = btake(&b); + + xgetenv(&b, "GO386"); + if(b.len == 0) { + if(cansse2()) + bwritestr(&b, "sse2"); + else + bwritestr(&b, "387"); + } + go386 = btake(&b); + p = bpathf(&b, "%s/include/u.h", goroot); if(!isfile(p)) { fatal("$GOROOT is not set correctly or not exported\n" @@ -126,6 +142,8 @@ init(void) xsetenv("GOROOT", goroot); xsetenv("GOARCH", goarch); xsetenv("GOOS", goos); + xsetenv("GOARM", goarm); + xsetenv("GO386", go386); // Make the environment more predictable. xsetenv("LANG", "C"); @@ -147,7 +165,7 @@ static void rmworkdir(void) { if(vflag > 1) - xprintf("rm -rf %s\n", workdir); + errprintf("rm -rf %s\n", workdir); xremoveall(workdir); } @@ -207,7 +225,7 @@ findgoversion(void) chomp(&branch); // What are the tags along the current branch? - tag = ""; + tag = "devel"; rev = "."; run(&b, goroot, CheckExit, "hg", "log", "-b", bstr(&branch), "-r", ".:0", "--template", "{tags} + ", nil); splitfields(&tags, bstr(&b)); @@ -216,7 +234,9 @@ findgoversion(void) p = tags.p[i]; if(streq(p, "+")) nrev++; - if(hasprefix(p, "release.") || hasprefix(p, "weekly.") || hasprefix(p, "go")) { + // NOTE: Can reenable the /* */ code when we want to + // start reporting versions named 'weekly' again. + if(/*hasprefix(p, "weekly.") ||*/ hasprefix(p, "go")) { tag = xstrdup(p); // If this tag matches the current checkout // exactly (no "+" yet), don't show extra @@ -236,7 +256,7 @@ findgoversion(void) if(rev[0]) { // Tag is before the revision we're building. // Add extra information. - run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short}", "-r", rev, nil); + run(&bmore, goroot, CheckExit, "hg", "log", "--template", " +{node|short} {date|date}", "-r", rev, nil); chomp(&bmore); } @@ -378,6 +398,9 @@ setup(void) // gccargs is the gcc command line to use for compiling a single C file. static char *proto_gccargs[] = { "-Wall", + // native Plan 9 compilers don't like non-standard prototypes + // so let gcc catch them. + "-Wstrict-prototypes", "-Wno-sign-compare", "-Wno-missing-braces", "-Wno-parentheses", @@ -387,7 +410,14 @@ static char *proto_gccargs[] = { "-Werror", "-fno-common", "-ggdb", + "-pipe", +#if defined(__NetBSD__) && defined(__arm__) + // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c + // Fix available at http://patchwork.ozlabs.org/patch/64562/. + "-O1", +#else "-O2", +#endif }; static Vec gccargs; @@ -474,14 +504,7 @@ static struct { "$GOROOT/pkg/obj/$GOOS_$GOARCH/libgc.a", }}, {"cmd/5l", { - "../ld/data.c", - "../ld/elf.c", - "../ld/go.c", - "../ld/ldelf.c", - "../ld/ldmacho.c", - "../ld/ldpe.c", - "../ld/lib.c", - "../ld/symtab.c", + "../ld/*", "enam.c", }}, {"cmd/6l", { @@ -535,17 +558,17 @@ static void install(char *dir) { char *name, *p, *elem, *prefix, *exe; - bool islib, ispkg, isgo, stale; + bool islib, ispkg, isgo, stale, clang; Buf b, b1, path; Vec compile, files, link, go, missing, clean, lib, extra; Time ttarg, t; - int i, j, k, n, doclean, targ; + int i, j, k, n, doclean, targ, usecpp; if(vflag) { if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) - xprintf("%s (%s/%s)\n", dir, goos, goarch); + errprintf("%s (%s/%s)\n", dir, goos, goarch); else - xprintf("%s\n", dir); + errprintf("%s\n", dir); } binit(&b); @@ -560,6 +583,7 @@ install(char *dir) vinit(&lib); vinit(&extra); + // path = full path to dir. bpathf(&path, "%s/src/%s", goroot, dir); name = lastelem(dir); @@ -574,7 +598,7 @@ install(char *dir) // For release, cmd/prof and cmd/cov are not included. if((streq(dir, "cmd/cov") || streq(dir, "cmd/prof")) && !isdir(bstr(&path))) { if(vflag > 1) - xprintf("skipping %s - does not exist\n", dir); + errprintf("skipping %s - does not exist\n", dir); goto out; } @@ -583,12 +607,13 @@ install(char *dir) xgetenv(&b, "CC"); if(b.len == 0) bprintf(&b, "gcc"); + clang = contains(bstr(&b), "clang"); splitfields(&gccargs, bstr(&b)); for(i=0; i<nelem(proto_gccargs); i++) vadd(&gccargs, proto_gccargs[i]); - if(xstrstr(bstr(&b), "clang") != nil) { - vadd(&gccargs, "-Wno-dangling-else"); - vadd(&gccargs, "-Wno-unused-value"); + if(clang) { + // clang is too smart about unused command-line arguments + vadd(&gccargs, "-Qunused-arguments"); } } @@ -605,7 +630,10 @@ install(char *dir) if(islib) { // C library. vadd(&link, "ar"); - vadd(&link, "rsc"); + if(streq(gohostos, "plan9")) + vadd(&link, "rc"); + else + vadd(&link, "rsc"); prefix = ""; if(!hasprefix(name, "lib")) prefix = "lib"; @@ -631,14 +659,21 @@ install(char *dir) vadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe)); } else { // C command. Use gccargs. - vcopy(&link, gccargs.p, gccargs.len); - vadd(&link, "-o"); - targ = link.len; - vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); - if(streq(gohostarch, "amd64")) - vadd(&link, "-m64"); - else if(streq(gohostarch, "386")) - vadd(&link, "-m32"); + if(streq(gohostos, "plan9")) { + vadd(&link, bprintf(&b, "%sl", gohostchar)); + vadd(&link, "-o"); + targ = link.len; + vadd(&link, bpathf(&b, "%s/%s", tooldir, name)); + } else { + vcopy(&link, gccargs.p, gccargs.len); + vadd(&link, "-o"); + targ = link.len; + vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); + if(streq(gohostarch, "amd64")) + vadd(&link, "-m64"); + else if(streq(gohostarch, "386")) + vadd(&link, "-m32"); + } } ttarg = mtime(link.p[targ]); @@ -672,6 +707,8 @@ install(char *dir) bsubst(&b1, "$GOARCH", goarch); p = bstr(&b1); if(hassuffix(p, ".a")) { + if(streq(gohostos, "plan9") && hassuffix(p, "libbio.a")) + continue; vadd(&lib, bpathf(&b, "%s", p)); continue; } @@ -741,6 +778,10 @@ install(char *dir) } files.len = n; + // If there are no files to compile, we're done. + if(files.len == 0) + goto out; + for(i=0; i<lib.len && !stale; i++) if(mtime(lib.p[i]) > ttarg) stale = 1; @@ -767,7 +808,7 @@ install(char *dir) for(j=0; j<nelem(gentab); j++) { if(hasprefix(elem, gentab[j].nameprefix)) { if(vflag > 1) - xprintf("generate %s\n", p); + errprintf("generate %s\n", p); gentab[j].gen(bstr(&path), p); // Do not add generated file to clean list. // In pkg/runtime, we want to be able to @@ -799,10 +840,10 @@ install(char *dir) p = files.p[i]; if(!hassuffix(p, ".goc")) continue; - // b = path/zp but with _goarch.c instead of .goc + // b = path/zp but with _goos_goarch.c instead of .goc bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p)); b.len -= 4; - bwritef(&b, "_%s.c", goarch); + bwritef(&b, "_%s_%s.c", goos, goarch); goc2c(p, bstr(&b)); vadd(&files, bstr(&b)); } @@ -812,10 +853,24 @@ install(char *dir) if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { // We've generated the right files; the go command can do the build. if(vflag > 1) - xprintf("skip build for cross-compile %s\n", dir); + errprintf("skip build for cross-compile %s\n", dir); goto nobuild; } + // The files generated by GNU Bison use macros that aren't + // supported by the Plan 9 compilers so we have to use the + // external preprocessor when compiling. + usecpp = 0; + if(streq(gohostos, "plan9")) { + for(i=0; i<files.len; i++) { + p = files.p[i]; + if(hassuffix(p, "y.tab.c") || hassuffix(p, "y.tab.h")){ + usecpp = 1; + break; + } + } + } + // Compile the files. for(i=0; i<files.len; i++) { if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s")) @@ -825,35 +880,53 @@ install(char *dir) vreset(&compile); if(!isgo) { // C library or tool. - vcopy(&compile, gccargs.p, gccargs.len); - vadd(&compile, "-c"); - if(streq(gohostarch, "amd64")) - vadd(&compile, "-m64"); - else if(streq(gohostarch, "386")) - vadd(&compile, "-m32"); - if(streq(dir, "lib9")) - vadd(&compile, "-DPLAN9PORT"); - - vadd(&compile, "-I"); - vadd(&compile, bpathf(&b, "%s/include", goroot)); + if(streq(gohostos, "plan9")) { + vadd(&compile, bprintf(&b, "%sc", gohostchar)); + vadd(&compile, "-FTVw"); + if(usecpp) + vadd(&compile, "-Bp+"); + vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot)); + vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch)); + } else { + vcopy(&compile, gccargs.p, gccargs.len); + vadd(&compile, "-c"); + if(streq(gohostarch, "amd64")) + vadd(&compile, "-m64"); + else if(streq(gohostarch, "386")) + vadd(&compile, "-m32"); + if(streq(dir, "lib9")) + vadd(&compile, "-DPLAN9PORT"); + + vadd(&compile, "-I"); + vadd(&compile, bpathf(&b, "%s/include", goroot)); + } vadd(&compile, "-I"); vadd(&compile, bstr(&path)); // lib9/goos.c gets the default constants hard-coded. if(streq(name, "goos.c")) { - vadd(&compile, bprintf(&b, "-DGOOS=\"%s\"", goos)); - vadd(&compile, bprintf(&b, "-DGOARCH=\"%s\"", goarch)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOOS=\"%s\"", goos)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOARCH=\"%s\"", goarch)); bprintf(&b1, "%s", goroot_final); bsubst(&b1, "\\", "\\\\"); // turn into C string - vadd(&compile, bprintf(&b, "-DGOROOT=\"%s\"", bstr(&b1))); - vadd(&compile, bprintf(&b, "-DGOVERSION=\"%s\"", goversion)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOROOT=\"%s\"", bstr(&b1))); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOVERSION=\"%s\"", goversion)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOARM=\"%s\"", goarm)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GO386=\"%s\"", go386)); } // gc/lex.c records the GOEXPERIMENT setting used during the build. if(streq(name, "lex.c")) { xgetenv(&b, "GOEXPERIMENT"); - vadd(&compile, bprintf(&b1, "-DGOEXPERIMENT=\"%s\"", bstr(&b))); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b1, "GOEXPERIMENT=\"%s\"", bstr(&b))); } } else { // Supporting files for a Go package. @@ -861,12 +934,16 @@ install(char *dir) vadd(&compile, bpathf(&b, "%s/%sa", tooldir, gochar)); else { vadd(&compile, bpathf(&b, "%s/%sc", tooldir, gochar)); - vadd(&compile, "-FVw"); + vadd(&compile, "-F"); + vadd(&compile, "-V"); + vadd(&compile, "-w"); } vadd(&compile, "-I"); vadd(&compile, workdir); - vadd(&compile, bprintf(&b, "-DGOOS_%s", goos)); - vadd(&compile, bprintf(&b, "-DGOARCH_%s", goarch)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOOS_%s", goos)); + vadd(&compile, "-D"); + vadd(&compile, bprintf(&b, "GOARCH_%s", goarch)); } bpathf(&b, "%s/%s", workdir, lastelem(files.p[i])); @@ -882,7 +959,11 @@ install(char *dir) doclean = 0; } - b.p[b.len-1] = 'o'; // was c or s + // Change the last character of the output file (which was c or s). + if(streq(gohostos, "plan9")) + b.p[b.len-1] = gohostchar[0]; + else + b.p[b.len-1] = 'o'; vadd(&compile, "-o"); vadd(&compile, bstr(&b)); vadd(&compile, files.p[i]); @@ -923,7 +1004,8 @@ install(char *dir) if(!islib && !isgo) { // C binaries need the libraries explicitly, and -lm. vcopy(&link, lib.p, lib.len); - vadd(&link, "-lm"); + if(!streq(gohostos, "plan9")) + vadd(&link, "-lm"); } // Remove target before writing it. @@ -981,6 +1063,20 @@ shouldbuild(char *file, char *dir) Buf b; Vec lines, fields; + // On Plan 9, most of the libraries are already present. + // The main exception is libmach which has been modified + // in various places to support Go object files. + if(streq(gohostos, "plan9")) { + if(streq(dir, "lib9")) { + name = lastelem(file); + if(streq(name, "goos.c") || streq(name, "flag.c")) + return 1; + return 0; + } + if(streq(dir, "libbio")) + return 0; + } + // Check file name for GOOS or GOARCH. name = lastelem(file); for(i=0; i<nelem(okgoos); i++) @@ -1057,7 +1153,7 @@ copy(char *dst, char *src, int exec) Buf b; if(vflag > 1) - xprintf("cp %s %s\n", src, dst); + errprintf("cp %s %s\n", src, dst); binit(&b); readfile(&b, src); @@ -1125,6 +1221,7 @@ static char *buildorder[] = { "pkg/go/ast", "pkg/go/parser", "pkg/os/exec", + "pkg/os/signal", "pkg/net/url", "pkg/text/template/parse", "pkg/text/template", @@ -1294,6 +1391,9 @@ cmdenv(int argc, char **argv) format = "%s=\"%s\"\n"; pflag = 0; ARGBEGIN{ + case '9': + format = "%s='%s'\n"; + break; case 'p': pflag = 1; break; @@ -1301,7 +1401,7 @@ cmdenv(int argc, char **argv) vflag++; break; case 'w': - format = "set %s=%s\n"; + format = "set %s=%s\r\n"; break; default: usage(); @@ -1318,6 +1418,10 @@ cmdenv(int argc, char **argv) xprintf(format, "GOHOSTOS", gohostos); xprintf(format, "GOTOOLDIR", tooldir); xprintf(format, "GOCHAR", gochar); + if(streq(goarch, "arm")) + xprintf(format, "GOARM", goarm); + if(streq(goarch, "386")) + xprintf(format, "GO386", go386); if(pflag) { sep = ":"; @@ -1511,7 +1615,7 @@ cmdbanner(int argc, char **argv) "Read and run ./sudo.bash to install the debuggers.\n"); } - if(!streq(goroot_final, goroot)) { + if(!xsamefile(goroot_final, goroot)) { xprintf("\n" "The binaries expect %s to be copied or moved to %s\n", goroot, goroot_final); diff --git a/src/cmd/dist/buildgc.c b/src/cmd/dist/buildgc.c index da38760c6..03a797f2c 100644 --- a/src/cmd/dist/buildgc.c +++ b/src/cmd/dist/buildgc.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. #include "a.h" -#include <stdio.h> /* * Helpers for building cmd/gc. diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index a0c62010d..f46f72d4b 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. #include "a.h" -#include <stdio.h> /* * Helpers for building pkg/runtime. @@ -20,6 +19,8 @@ mkzversion(char *dir, char *file) { Buf b, out; + USED(dir); + binit(&b); binit(&out); @@ -46,6 +47,8 @@ void mkzgoarch(char *dir, char *file) { Buf b, out; + + USED(dir); binit(&b); binit(&out); @@ -72,6 +75,8 @@ void mkzgoos(char *dir, char *file) { Buf b, out; + + USED(dir); binit(&b); binit(&out); @@ -100,9 +105,15 @@ static struct { "#define m(r) 4(r)\n" }, {"386", "plan9", + "// Plan 9 does not have per-process segment descriptors with\n" + "// which to do thread-local storage. Instead, we will use a\n" + "// fixed offset from the per-process TOS struct address for\n" + "// the local storage. Since the process ID is contained in the\n" + "// TOS struct, we specify an offset for that here as well.\n" "#define get_tls(r) MOVL _tos(SB), r \n" "#define g(r) -8(r)\n" "#define m(r) -4(r)\n" + "#define procid(r) 48(r)\n" }, {"386", "linux", "// On Linux systems, what we call 0(GS) and 4(GS) for g and m\n" @@ -140,6 +151,12 @@ static struct { "#define g(r) 0(r)\n" "#define m(r) 8(r)\n" }, + {"amd64", "plan9", + "#define get_tls(r)\n" + "#define g(r) 0(GS)\n" + "#define m(r) 8(GS)\n" + "#define procid(r) 16(GS)\n" + }, {"amd64", "", "// The offsets 0 and 8 are known to:\n" "// ../../cmd/6l/pass.c:/D_GS\n" @@ -188,16 +205,23 @@ mkzasm(char *dir, char *file) fatal("unknown $GOOS/$GOARCH in mkzasm"); ok: - // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -a proc.c + // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c // to get acid [sic] output. vreset(&argv); vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); - vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); - vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); - vadd(&argv, bprintf(&b, "-I%s", workdir)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOOS_%s", goos)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s", workdir)); vadd(&argv, "-a"); + vadd(&argv, "-n"); + vadd(&argv, "-o"); + vadd(&argv, bpathf(&b, "%s/proc.acid", workdir)); vadd(&argv, "proc.c"); - runv(&in, dir, CheckExit, &argv); + runv(nil, dir, CheckExit, &argv); + readfile(&in, bpathf(&b, "%s/proc.acid", workdir)); // Convert input like // aggr G @@ -222,6 +246,8 @@ ok: aggr = "gobuf"; else if(streq(fields.p[1], "WinCall")) aggr = "wincall"; + else if(streq(fields.p[1], "SEH")) + aggr = "seh"; } if(hasprefix(lines.p[i], "}")) aggr = nil; @@ -251,6 +277,7 @@ static char *runtimedefs[] = { "iface.c", "hashmap.c", "chan.c", + "parfor.c", }; // mkzruntimedefs writes zruntime_defs_$GOOS_$GOARCH.h, @@ -265,11 +292,12 @@ mkzruntimedefs(char *dir, char *file) { int i, skip; char *p; - Buf in, b, out; + Buf in, b, b1, out; Vec argv, lines, fields, seen; binit(&in); binit(&b); + binit(&b1); binit(&out); vinit(&argv); vinit(&lines); @@ -285,18 +313,25 @@ mkzruntimedefs(char *dir, char *file) ); - // Run 6c -DGOOS_goos -DGOARCH_goarch -Iworkdir -q + // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs // on each of the runtimedefs C files. vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar)); - vadd(&argv, bprintf(&b, "-DGOOS_%s", goos)); - vadd(&argv, bprintf(&b, "-DGOARCH_%s", goarch)); - vadd(&argv, bprintf(&b, "-I%s", workdir)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOOS_%s", goos)); + vadd(&argv, "-D"); + vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s", workdir)); vadd(&argv, "-q"); + vadd(&argv, "-n"); + vadd(&argv, "-o"); + vadd(&argv, bpathf(&b, "%s/runtimedefs", workdir)); vadd(&argv, ""); p = argv.p[argv.len-1]; for(i=0; i<nelem(runtimedefs); i++) { argv.p[argv.len-1] = runtimedefs[i]; - runv(&b, dir, CheckExit, &argv); + runv(nil, dir, CheckExit, &argv); + readfile(&b, bpathf(&b1, "%s/runtimedefs", workdir)); bwriteb(&in, &b); } argv.p[argv.len-1] = p; @@ -338,6 +373,7 @@ mkzruntimedefs(char *dir, char *file) bfree(&in); bfree(&b); + bfree(&b1); bfree(&out); vfree(&argv); vfree(&lines); diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c index 22f72f8b5..a103bce8d 100644 --- a/src/cmd/dist/goc2c.c +++ b/src/cmd/dist/goc2c.c @@ -26,6 +26,11 @@ static char *input; static Buf *output; #define EOF -1 +enum +{ + use64bitint = 1, +}; + static int xgetchar(void) { @@ -86,13 +91,16 @@ static struct { char *name; int size; } type_table[] = { - /* variable sized first, for easy replacement */ - /* order matches enum above */ - /* default is 32-bit architecture sizes */ - {"bool", 1}, + /* + * variable sized first, for easy replacement. + * order matches enum above. + * default is 32-bit architecture sizes. + * spelling as in package runtime, so intgo/uintgo not int/uint. + */ + {"bool", 1}, {"float", 4}, - {"int", 4}, - {"uint", 4}, + {"intgo", 4}, + {"uintgo", 4}, {"uintptr", 4}, {"String", 8}, {"Slice", 12}, @@ -101,17 +109,18 @@ static struct { /* fixed size */ {"float32", 4}, {"float64", 8}, - {"byte", 1}, - {"int8", 1}, + {"byte", 1}, + {"int8", 1}, {"uint8", 1}, {"int16", 2}, {"uint16", 2}, {"int32", 4}, + {"rune", 4}, {"uint32", 4}, {"int64", 8}, {"uint64", 8}, - {nil}, + {nil, 0}, }; /* Fixed structure alignment (non-gcc only) */ @@ -328,7 +337,7 @@ read_type(void) unsigned int len; p = read_token_no_eof(); - if (*p != '*') + if (*p != '*' && !streq(p, "int") && !streq(p, "uint")) return p; op = p; pointer_count = 0; @@ -337,13 +346,18 @@ read_type(void) ++p; } len = xstrlen(p); - q = xmalloc(len + pointer_count + 1); + q = xmalloc(len + 2 + pointer_count + 1); xmemmove(q, p, len); - while (pointer_count > 0) { - q[len] = '*'; - ++len; - --pointer_count; + + // Turn int/uint into intgo/uintgo. + if((len == 3 && xmemcmp(q, "int", 3) == 0) || (len == 4 && xmemcmp(q, "uint", 4) == 0)) { + q[len++] = 'g'; + q[len++] = 'o'; } + + while (pointer_count-- > 0) + q[len++] = '*'; + q[len] = '\0'; xfree(op); return q; @@ -570,8 +584,9 @@ write_gcc_func_header(char *package, char *name, struct params *params, static void write_gcc_func_trailer(char *package, char *name, struct params *rets) { - if (rets == nil) - ; + if (rets == nil) { + // nothing to do + } else if (rets->next == nil) bwritef(output, "return %s;\n", rets->name); else { @@ -712,15 +727,25 @@ goc2c(char *goc, char *c) if(!gcc) { if(streq(goarch, "amd64")) { type_table[Uintptr].size = 8; - type_table[String].size = 16; - type_table[Slice].size = 8+4+4; type_table[Eface].size = 8+8; + type_table[String].size = 16; + if(use64bitint) { + type_table[Int].size = 8; + type_table[Uint].size = 8; + } + type_table[Slice].size = 8+2*type_table[Int].size; structround = 8; } else { + // NOTE: These are set in the initializer, + // but they might have been changed by a + // previous invocation of goc2c, so we have + // to restore them. type_table[Uintptr].size = 4; type_table[String].size = 8; type_table[Slice].size = 16; type_table[Eface].size = 4+4; + type_table[Int].size = 4; + type_table[Uint].size = 4; structround = 4; } } diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c new file mode 100644 index 000000000..8a7c0ab1c --- /dev/null +++ b/src/cmd/dist/plan9.c @@ -0,0 +1,764 @@ +// 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. + +// These #ifdefs are being used as a substitute for +// build configuration, so that on any system, this +// tool can be built with the local equivalent of +// cc *.c +// +#ifdef PLAN9 + +#include <u.h> +#include <libc.h> +#include <stdio.h> +#undef nil +#undef nelem +#include "a.h" + +// bprintf replaces the buffer with the result of the printf formatting +// and returns a pointer to the NUL-terminated buffer contents. +char* +bprintf(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + return bstr(b); +} + +// bpathf is the same as bprintf (on windows it turns / into \ after the printf). +// It returns a pointer to the NUL-terminated buffer contents. +char* +bpathf(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + breset(b); + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); + return bstr(b); +} + +// bwritef is like bprintf but does not reset the buffer +// and does not return the NUL-terminated string. +void +bwritef(Buf *b, char *fmt, ...) +{ + va_list arg; + char buf[4096]; + + va_start(arg, fmt); + vsnprintf(buf, sizeof buf, fmt, arg); + va_end(arg); + bwritestr(b, buf); +} + +// breadfrom appends to b all the data that can be read from fd. +static void +breadfrom(Buf *b, int fd) +{ + int n; + + for(;;) { + bgrow(b, 4096); + n = read(fd, b->p+b->len, 4096); + if(n < 0) + fatal("read"); + if(n == 0) + break; + b->len += n; + } +} + +// xgetenv replaces b with the value of the named environment variable. +void +xgetenv(Buf *b, char *name) +{ + char *p; + + breset(b); + p = getenv(name); + if(p != nil) + bwritestr(b, p); +} + +static void genrun(Buf *b, char *dir, int mode, Vec *argv, int bg); + +// run runs the command named by cmd. +// If b is not nil, run replaces b with the output of the command. +// If dir is not nil, run runs the command in that directory. +// If mode is CheckExit, run calls fatal if the command is not successful. +void +run(Buf *b, char *dir, int mode, char *cmd, ...) +{ + va_list arg; + Vec argv; + char *p; + + vinit(&argv); + vadd(&argv, cmd); + va_start(arg, cmd); + while((p = va_arg(arg, char*)) != nil) + vadd(&argv, p); + va_end(arg); + + runv(b, dir, mode, &argv); + + vfree(&argv); +} + +// runv is like run but takes a vector. +void +runv(Buf *b, char *dir, int mode, Vec *argv) +{ + genrun(b, dir, mode, argv, 1); +} + +// bgrunv is like run but runs the command in the background. +// bgwait waits for pending bgrunv to finish. +void +bgrunv(char *dir, int mode, Vec *argv) +{ + genrun(nil, dir, mode, argv, 0); +} + +#define MAXBG 4 /* maximum number of jobs to run at once */ + +static struct { + int pid; + int mode; + char *cmd; + Buf *b; +} bg[MAXBG]; +static int nbg; +static int maxnbg = nelem(bg); + +static void bgwait1(void); + +// genrun is the generic run implementation. +static void +genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) +{ + int i, p[2], pid; + Buf b1, cmd; + char *q; + + while(nbg >= maxnbg) + bgwait1(); + + binit(&b1); + binit(&cmd); + + if(!isabs(argv->p[0])) { + bpathf(&b1, "/bin/%s", argv->p[0]); + free(argv->p[0]); + argv->p[0] = xstrdup(bstr(&b1)); + } + + // Generate a copy of the command to show in a log. + // Substitute $WORK for the work directory. + for(i=0; i<argv->len; i++) { + if(i > 0) + bwritestr(&cmd, " "); + q = argv->p[i]; + if(workdir != nil && hasprefix(q, workdir)) { + bwritestr(&cmd, "$WORK"); + q += strlen(workdir); + } + bwritestr(&cmd, q); + } + if(vflag > 1) + errprintf("%s\n", bstr(&cmd)); + + if(b != nil) { + breset(b); + if(pipe(p) < 0) + fatal("pipe"); + } + + switch(pid = fork()) { + case -1: + fatal("fork"); + case 0: + if(b != nil) { + close(0); + close(p[0]); + dup(p[1], 1); + dup(p[1], 2); + if(p[1] > 2) + close(p[1]); + } + if(dir != nil) { + if(chdir(dir) < 0) { + fprint(2, "chdir: %r\n"); + _exits("chdir"); + } + } + vadd(argv, nil); + exec(argv->p[0], argv->p); + fprint(2, "%s\n", bstr(&cmd)); + fprint(2, "exec: %r\n"); + _exits("exec"); + } + if(b != nil) { + close(p[1]); + breadfrom(b, p[0]); + close(p[0]); + } + + if(nbg < 0) + fatal("bad bookkeeping"); + bg[nbg].pid = pid; + bg[nbg].mode = mode; + bg[nbg].cmd = btake(&cmd); + bg[nbg].b = b; + nbg++; + + if(wait) + bgwait(); + + bfree(&cmd); + bfree(&b1); +} + +// bgwait1 waits for a single background job. +static void +bgwait1(void) +{ + Waitmsg *w; + int i, mode; + char *cmd; + Buf *b; + + w = wait(); + if(w == nil) + fatal("wait"); + + for(i=0; i<nbg; i++) + if(bg[i].pid == w->pid) + goto ok; + fatal("wait: unexpected pid"); + +ok: + cmd = bg[i].cmd; + mode = bg[i].mode; + bg[i].pid = 0; + b = bg[i].b; + bg[i].b = nil; + bg[i] = bg[--nbg]; + + if(mode == CheckExit && w->msg[0]) { + if(b != nil) + xprintf("%s\n", bstr(b)); + fatal("FAILED: %s", cmd); + } + xfree(cmd); +} + +// bgwait waits for all the background jobs. +void +bgwait(void) +{ + while(nbg > 0) + bgwait1(); +} + +// xgetwd replaces b with the current directory. +void +xgetwd(Buf *b) +{ + char buf[4096]; + + breset(b); + if(getwd(buf, sizeof buf) == nil) + fatal("getwd"); + bwritestr(b, buf); +} + +// xrealwd replaces b with the 'real' name for the given path. +// real is defined as what getcwd returns in that directory. +void +xrealwd(Buf *b, char *path) +{ + char buf[4096]; + int fd; + + fd = open(path, OREAD); + if(fd2path(fd, buf, sizeof buf) < 0) + fatal("fd2path"); + close(fd); + breset(b); + bwritestr(b, buf); +} + +// isdir reports whether p names an existing directory. +bool +isdir(char *p) +{ + Dir *d; + ulong mode; + + d = dirstat(p); + if(d == nil) + return 0; + mode = d->mode; + free(d); + return (mode & DMDIR) == DMDIR; +} + +// isfile reports whether p names an existing file. +bool +isfile(char *p) +{ + Dir *d; + ulong mode; + + d = dirstat(p); + if(d == nil) + return 0; + mode = d->mode; + free(d); + return (mode & DMDIR) == 0; +} + +// mtime returns the modification time of the file p. +Time +mtime(char *p) +{ + Dir *d; + ulong t; + + d = dirstat(p); + if(d == nil) + return 0; + t = d->mtime; + free(d); + return (Time)t; +} + +// isabs reports whether p is an absolute path. +bool +isabs(char *p) +{ + return hasprefix(p, "/"); +} + +// readfile replaces b with the content of the named file. +void +readfile(Buf *b, char *file) +{ + int fd; + + breset(b); + fd = open(file, OREAD); + if(fd < 0) + fatal("open %s", file); + breadfrom(b, fd); + close(fd); +} + +// writefile writes b to the named file, creating it if needed. +void +writefile(Buf *b, char *file, int exec) +{ + int fd; + Dir d; + + fd = create(file, ORDWR, 0666); + if(fd < 0) + fatal("create %s", file); + if(write(fd, b->p, b->len) != b->len) + fatal("short write"); + if(exec) { + nulldir(&d); + d.mode = 0755; + dirfwstat(fd, &d); + } + close(fd); +} + +// xmkdir creates the directory p. +void +xmkdir(char *p) +{ + int fd; + + if(isdir(p)) + return; + fd = create(p, OREAD, 0777|DMDIR); + close(fd); + if(fd < 0) + fatal("mkdir %s", p); +} + +// xmkdirall creates the directory p and its parents, as needed. +void +xmkdirall(char *p) +{ + char *q; + + if(isdir(p)) + return; + q = strrchr(p, '/'); + if(q != nil) { + *q = '\0'; + xmkdirall(p); + *q = '/'; + } + xmkdir(p); +} + +// xremove removes the file p. +void +xremove(char *p) +{ + if(vflag > 2) + errprintf("rm %s\n", p); + remove(p); +} + +// xremoveall removes the file or directory tree rooted at p. +void +xremoveall(char *p) +{ + int i; + Buf b; + Vec dir; + + binit(&b); + vinit(&dir); + + if(isdir(p)) { + xreaddir(&dir, p); + for(i=0; i<dir.len; i++) { + bprintf(&b, "%s/%s", p, dir.p[i]); + xremoveall(bstr(&b)); + } + } + if(vflag > 2) + errprintf("rm %s\n", p); + remove(p); + + bfree(&b); + vfree(&dir); +} + +// xreaddir replaces dst with a list of the names of the files in dir. +// The names are relative to dir; they are not full paths. +void +xreaddir(Vec *dst, char *dir) +{ + Dir *d; + int fd, i, n; + + vreset(dst); + + fd = open(dir, OREAD); + if(fd < 0) + fatal("open %s", dir); + n = dirreadall(fd, &d); + for(i=0; i<n; i++) + vadd(dst, d[i].name); + free(d); + close(fd); +} + +// xworkdir creates a new temporary directory to hold object files +// and returns the name of that directory. +char* +xworkdir(void) +{ + Buf b; + char *p; + int fd, tries; + + binit(&b); + + fd = 0; + for(tries=0; tries<1000; tries++) { + bprintf(&b, "/tmp/go-cbuild-%06x", nrand((1<<24)-1)); + fd = create(bstr(&b), OREAD|OEXCL, 0700|DMDIR); + if(fd >= 0) + goto done; + } + fatal("xworkdir create"); + +done: + close(fd); + p = btake(&b); + + bfree(&b); + return p; +} + +// fatal prints an error message to standard error and exits. +void +fatal(char *msg, ...) +{ + char buf[ERRMAX]; + va_list arg; + + rerrstr(buf, sizeof buf); + + fflush(stdout); + fprintf(stderr, "go tool dist: "); + va_start(arg, msg); + vfprintf(stderr, msg, arg); + va_end(arg); + + if(buf[0]) + fprintf(stderr, ": %s", buf); + fprintf(stderr, "\n"); + + bgwait(); + exits(msg); +} + +// xmalloc returns a newly allocated zeroed block of n bytes of memory. +// It calls fatal if it runs out of memory. +void* +xmalloc(int n) +{ + void *p; + + p = malloc(n); + if(p == nil) + fatal("out of memory"); + memset(p, 0, n); + return p; +} + +// xstrdup returns a newly allocated copy of p. +// It calls fatal if it runs out of memory. +char* +xstrdup(char *p) +{ + p = strdup(p); + if(p == nil) + fatal("out of memory"); + return p; +} + +// xrealloc grows the allocation p to n bytes and +// returns the new (possibly moved) pointer. +// It calls fatal if it runs out of memory. +void* +xrealloc(void *p, int n) +{ + p = realloc(p, n); + if(p == nil) + fatal("out of memory"); + return p; +} + +// xfree frees the result returned by xmalloc, xstrdup, or xrealloc. +void +xfree(void *p) +{ + free(p); +} + +// hassuffix reports whether p ends with suffix. +bool +hassuffix(char *p, char *suffix) +{ + int np, ns; + + np = strlen(p); + ns = strlen(suffix); + return np >= ns && strcmp(p+np-ns, suffix) == 0; +} + +// hasprefix reports whether p begins wtih prefix. +bool +hasprefix(char *p, char *prefix) +{ + return strncmp(p, prefix, strlen(prefix)) == 0; +} + +// contains reports whether sep appears in p. +bool +contains(char *p, char *sep) +{ + return strstr(p, sep) != nil; +} + +// streq reports whether p and q are the same string. +bool +streq(char *p, char *q) +{ + return strcmp(p, q) == 0; +} + +// lastelem returns the final path element in p. +char* +lastelem(char *p) +{ + char *out; + + out = p; + for(; *p; p++) + if(*p == '/') + out = p+1; + return out; +} + +// xmemmove copies n bytes from src to dst. +void +xmemmove(void *dst, void *src, int n) +{ + memmove(dst, src, n); +} + +// xmemcmp compares the n-byte regions starting at a and at b. +int +xmemcmp(void *a, void *b, int n) +{ + return memcmp(a, b, n); +} + +// xstrlen returns the length of the NUL-terminated string at p. +int +xstrlen(char *p) +{ + return strlen(p); +} + +// xexit exits the process with return code n. +void +xexit(int n) +{ + char buf[32]; + + snprintf(buf, sizeof buf, "%d", n); + exits(buf); +} + +// xatexit schedules the exit-handler f to be run when the program exits. +void +xatexit(void (*f)(void)) +{ + atexit(f); +} + +// xprintf prints a message to standard output. +void +xprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vprintf(fmt, arg); + va_end(arg); +} + +// errprintf prints a message to standard output. +void +errprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); +} + +// xsetenv sets the environment variable $name to the given value. +void +xsetenv(char *name, char *value) +{ + putenv(name, value); +} + +// main takes care of OS-specific startup and dispatches to xmain. +void +main(int argc, char **argv) +{ + Buf b; + + setvbuf(stdout, nil, _IOLBF, BUFSIZ); + setvbuf(stderr, nil, _IOLBF, BUFSIZ); + + binit(&b); + + rfork(RFENVG); + + slash = "/"; + gohostos = "plan9"; + + xgetenv(&b, "objtype"); + if(b.len == 0) + fatal("$objtype is unset"); + gohostarch = btake(&b); + + srand(time(0)+getpid()); + init(); + xmain(argc, argv); + + bfree(&b); + exits(nil); +} + +// xqsort is a wrapper for the C standard qsort. +void +xqsort(void *data, int n, int elemsize, int (*cmp)(const void*, const void*)) +{ + qsort(data, n, elemsize, cmp); +} + +// xstrcmp compares the NUL-terminated strings a and b. +int +xstrcmp(char *a, char *b) +{ + return strcmp(a, b); +} + +// xstrstr returns a pointer to the first occurrence of b in a. +char* +xstrstr(char *a, char *b) +{ + return strstr(a, b); +} + +// xstrrchr returns a pointer to the final occurrence of c in p. +char* +xstrrchr(char *p, int c) +{ + return strrchr(p, c); +} + +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + return streq(f1, f2); // suffice for now +} + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +int +xtryexecfunc(void (*f)(void)) +{ + USED(f); + return 0; // suffice for now +} + +bool +cansse2(void) +{ + // if we had access to cpuid, could answer this question + // less conservatively. + return 0; +} + +#endif // PLAN9 diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c index e6d82e14e..e4e2dcc9f 100644 --- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -23,6 +23,7 @@ #include <stdlib.h> #include <errno.h> #include <stdarg.h> +#include <setjmp.h> // bprintf replaces the buffer with the result of the printf formatting // and returns a pointer to the NUL-terminated buffer contents. @@ -177,7 +178,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) bwritestr(&cmd, q); } if(vflag > 1) - xprintf("%s\n", bstr(&cmd)); + errprintf("%s\n", bstr(&cmd)); if(b != nil) { breset(b); @@ -398,7 +399,7 @@ void xremove(char *p) { if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); unlink(p); } @@ -420,11 +421,11 @@ xremoveall(char *p) xremoveall(bstr(&b)); } if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); rmdir(p); } else { if(vflag > 2) - xprintf("rm %s\n", p); + errprintf("rm %s\n", p); unlink(p); } @@ -627,6 +628,17 @@ xprintf(char *fmt, ...) va_end(arg); } +// errprintf prints a message to standard output. +void +errprintf(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); +} + // xsetenv sets the environment variable $name to the given value. void xsetenv(char *name, char *value) @@ -658,6 +670,10 @@ main(int argc, char **argv) gohostos = "linux"; #elif defined(__FreeBSD__) gohostos = "freebsd"; +#elif defined(__FreeBSD_kernel__) + // detect debian/kFreeBSD. + // http://wiki.debian.org/Debian_GNU/kFreeBSD_FAQ#Q._How_do_I_detect_kfreebsd_with_preprocessor_directives_in_a_C_program.3F + gohostos = "freebsd"; #elif defined(__OpenBSD__) gohostos = "openbsd"; #elif defined(__NetBSD__) @@ -716,5 +732,76 @@ xstrrchr(char *p, int c) return strrchr(p, c); } +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + return streq(f1, f2); // suffice for now +} + +sigjmp_buf sigill_jmpbuf; +static void sigillhand(int); + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +// Some systems (notably NetBSD) will spin and spin when executing VFPv3 +// instructions on VFPv2 system (e.g. Raspberry Pi) without ever triggering +// SIGILL, so we set a 1-second alarm to catch that case. +int +xtryexecfunc(void (*f)(void)) +{ + int r; + r = 0; + signal(SIGILL, sigillhand); + signal(SIGALRM, sigillhand); + alarm(1); + if(sigsetjmp(sigill_jmpbuf, 1) == 0) { + f(); + r = 1; + } + signal(SIGILL, SIG_DFL); + alarm(0); + signal(SIGALRM, SIG_DFL); + return r; +} + +// SIGILL handler helper +static void +sigillhand(int signum) +{ + USED(signum); + siglongjmp(sigill_jmpbuf, 1); +} + +static void +__cpuid(int dst[4], int ax) +{ +#ifdef __i386__ + // we need to avoid ebx on i386 (esp. when -fPIC). + asm volatile( + "mov %%ebx, %%edi\n\t" + "cpuid\n\t" + "xchgl %%ebx, %%edi" + : "=a" (dst[0]), "=D" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#elif defined(__x86_64__) + asm volatile("cpuid" + : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#else + dst[0] = dst[1] = dst[2] = dst[3] = 0; +#endif +} + +bool +cansse2(void) +{ + int info[4]; + + __cpuid(info, 1); + return (info[3] & (1<<26)) != 0; // SSE2 +} + #endif // PLAN9 #endif // __WINDOWS__ diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c index 557e4b003..7bcda4508 100644 --- a/src/cmd/dist/windows.c +++ b/src/cmd/dist/windows.c @@ -115,7 +115,7 @@ errstr(void) binit(&b); code = GetLastError(); r = nil; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, nil, code, 0, (Rune*)&r, 0, nil); toutf(&b, r); return bstr(&b); // leak but we're dying anyway @@ -285,9 +285,11 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) binit(&cmd); for(i=0; i<argv->len; i++) { + q = argv->p[i]; + if(i == 0 && streq(q, "hg")) + bwritestr(&cmd, "cmd.exe /c "); if(i > 0) bwritestr(&cmd, " "); - q = argv->p[i]; if(contains(q, " ") || contains(q, "\t") || contains(q, "\"") || contains(q, "\\\\") || hassuffix(q, "\\")) { bwritestr(&cmd, "\""); nslash = 0; @@ -314,7 +316,7 @@ genrun(Buf *b, char *dir, int mode, Vec *argv, int wait) } } if(vflag > 1) - xprintf("%s\n", bstr(&cmd)); + errprintf("%s\n", bstr(&cmd)); torune(&rcmd, bstr(&cmd)); rexe = nil; @@ -528,8 +530,9 @@ readfile(Buf *b, char *file) HANDLE h; Rune *r; + breset(b); if(vflag > 2) - xprintf("read %s\n", file); + errprintf("read %s\n", file); torune(&r, file); h = CreateFileW(r, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); if(h == INVALID_HANDLE_VALUE) @@ -548,7 +551,7 @@ writefile(Buf *b, char *file, int exec) USED(exec); if(vflag > 2) - xprintf("write %s\n", file); + errprintf("write %s\n", file); torune(&r, file); h = CreateFileW(r, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0); if(h == INVALID_HANDLE_VALUE) @@ -707,7 +710,7 @@ fatal(char *msg, ...) vsnprintf(buf1, sizeof buf1, msg, arg); va_end(arg); - xprintf("go tool dist: %s\n", buf1); + errprintf("go tool dist: %s\n", buf1); bgwait(); ExitProcess(1); @@ -848,6 +851,23 @@ xprintf(char *fmt, ...) xfree(p); } +void +errprintf(char *fmt, ...) +{ + va_list arg; + char *p; + DWORD n, w; + + va_start(arg, fmt); + n = vsnprintf(NULL, 0, fmt, arg); + p = xmalloc(n+1); + vsnprintf(p, n+1, fmt, arg); + va_end(arg); + w = 0; + WriteFile(GetStdHandle(STD_ERROR_HANDLE), p, n, &w, 0); + xfree(p); +} + int main(int argc, char **argv) { @@ -907,4 +927,75 @@ xstrrchr(char *p, int c) return nil; } +// xsamefile returns whether f1 and f2 are the same file (or dir) +int +xsamefile(char *f1, char *f2) +{ + Rune *ru; + HANDLE fd1, fd2; + BY_HANDLE_FILE_INFORMATION fi1, fi2; + int r; + + // trivial case + if(streq(f1, f2)) + return 1; + + torune(&ru, f1); + // refer to ../../pkg/os/stat_windows.go:/sameFile + fd1 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + xfree(ru); + if(fd1 == INVALID_HANDLE_VALUE) + return 0; + torune(&ru, f2); + fd2 = CreateFileW(ru, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + xfree(ru); + if(fd2 == INVALID_HANDLE_VALUE) { + CloseHandle(fd1); + return 0; + } + r = GetFileInformationByHandle(fd1, &fi1) != 0 && GetFileInformationByHandle(fd2, &fi2) != 0; + CloseHandle(fd2); + CloseHandle(fd1); + if(r != 0 && + fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber && + fi1.nFileIndexHigh == fi2.nFileIndexHigh && + fi1.nFileIndexLow == fi2.nFileIndexLow) + return 1; + return 0; +} + +// xtryexecfunc tries to execute function f, if any illegal instruction +// signal received in the course of executing that function, it will +// return 0, otherwise it will return 1. +int +xtryexecfunc(void (*f)(void)) +{ + return 0; // suffice for now +} + +static void +cpuid(int dst[4], int ax) +{ + // NOTE: This asm statement is for mingw. + // If we ever support MSVC, use __cpuid(dst, ax) + // to use the built-in. +#if defined(__i386__) || defined(__x86_64__) + asm volatile("cpuid" + : "=a" (dst[0]), "=b" (dst[1]), "=c" (dst[2]), "=d" (dst[3]) + : "0" (ax)); +#else + dst[0] = dst[1] = dst[2] = dst[3] = 0; +#endif +} + +bool +cansse2(void) +{ + int info[4]; + + cpuid(info, 1); + return (info[3] & (1<<26)) != 0; // SSE2 +} + + #endif // __WINDOWS__ diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go index a92e0fc06..5de3e08c5 100644 --- a/src/cmd/fix/doc.go +++ b/src/cmd/fix/doc.go @@ -33,4 +33,4 @@ Fix does not make backup copies of the files that it edits. Instead, use a version control system's ``diff'' functionality to inspect the changes that fix makes before committing them. */ -package documentation +package main diff --git a/src/cmd/fix/error.go b/src/cmd/fix/error.go deleted file mode 100644 index 55613210a..000000000 --- a/src/cmd/fix/error.go +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "regexp" - "strings" -) - -func init() { - register(errorFix) -} - -var errorFix = fix{ - "error", - "2011-11-02", - errorFn, - `Use error instead of os.Error. - -This fix rewrites code using os.Error to use error: - - os.Error -> error - os.NewError -> errors.New - os.EOF -> io.EOF - -Seeing the old names above (os.Error and so on) triggers the following -heuristic rewrites. The heuristics can be forced using the -force=error flag. - -A top-level function, variable, or constant named error is renamed error_. - -Error implementations—those types used as os.Error or named -XxxError—have their String methods renamed to Error. Any existing -Error field or method is renamed to Err. - -Error values—those with type os.Error or named e, err, error, err1, -and so on—have method calls and field references rewritten just -as the types do (String to Error, Error to Err). Also, a type assertion -of the form err.(*os.Waitmsg) becomes err.(*exec.ExitError). - -http://codereview.appspot.com/5305066 -`, -} - -// At minimum, this fix applies the following rewrites: -// -// os.Error -> error -// os.NewError -> errors.New -// os.EOF -> io.EOF -// -// However, if can apply any of those rewrites, it assumes that the -// file predates the error type and tries to update the code to use -// the new definition for error - an Error method, not a String method. -// This more heuristic procedure may not be 100% accurate, so it is -// only run when the file needs updating anyway. The heuristic can -// be forced to run using -force=error. -// -// First, we must identify the implementations of os.Error. -// These include the type of any value returned as or assigned to an os.Error. -// To that set we add any type whose name contains "Error" or "error". -// The heuristic helps for implementations that are not used as os.Error -// in the file in which they are defined. -// -// In any implementation of os.Error, we rename an existing struct field -// or method named Error to Err and rename the String method to Error. -// -// Second, we must identify the values of type os.Error. -// These include any value that obviously has type os.Error. -// To that set we add any variable whose name is e or err or error -// possibly followed by _ or a numeric or capitalized suffix. -// The heuristic helps for variables that are initialized using calls -// to functions in other packages. The type checker does not have -// information about those packages available, and in general cannot -// (because the packages may themselves not compile). -// -// For any value of type os.Error, we replace a call to String with a call to Error. -// We also replace type assertion err.(*os.Waitmsg) with err.(*exec.ExitError). - -// Variables matching this regexp are assumed to have type os.Error. -var errVar = regexp.MustCompile(`^(e|err|error)_?([A-Z0-9].*)?$`) - -// Types matching this regexp are assumed to be implementations of os.Error. -var errType = regexp.MustCompile(`^\*?([Ee]rror|.*Error)$`) - -// Type-checking configuration: tell the type-checker this basic -// information about types, functions, and variables in external packages. -var errorTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "os.Error": {}, - }, - Func: map[string]string{ - "fmt.Errorf": "os.Error", - "os.NewError": "os.Error", - }, - Var: map[string]string{ - "os.EPERM": "os.Error", - "os.ENOENT": "os.Error", - "os.ESRCH": "os.Error", - "os.EINTR": "os.Error", - "os.EIO": "os.Error", - "os.ENXIO": "os.Error", - "os.E2BIG": "os.Error", - "os.ENOEXEC": "os.Error", - "os.EBADF": "os.Error", - "os.ECHILD": "os.Error", - "os.EDEADLK": "os.Error", - "os.ENOMEM": "os.Error", - "os.EACCES": "os.Error", - "os.EFAULT": "os.Error", - "os.EBUSY": "os.Error", - "os.EEXIST": "os.Error", - "os.EXDEV": "os.Error", - "os.ENODEV": "os.Error", - "os.ENOTDIR": "os.Error", - "os.EISDIR": "os.Error", - "os.EINVAL": "os.Error", - "os.ENFILE": "os.Error", - "os.EMFILE": "os.Error", - "os.ENOTTY": "os.Error", - "os.EFBIG": "os.Error", - "os.ENOSPC": "os.Error", - "os.ESPIPE": "os.Error", - "os.EROFS": "os.Error", - "os.EMLINK": "os.Error", - "os.EPIPE": "os.Error", - "os.EAGAIN": "os.Error", - "os.EDOM": "os.Error", - "os.ERANGE": "os.Error", - "os.EADDRINUSE": "os.Error", - "os.ECONNREFUSED": "os.Error", - "os.ENAMETOOLONG": "os.Error", - "os.EAFNOSUPPORT": "os.Error", - "os.ETIMEDOUT": "os.Error", - "os.ENOTCONN": "os.Error", - }, -} - -func errorFn(f *ast.File) bool { - if !imports(f, "os") && !force["error"] { - return false - } - - // Fix gets called once to run the heuristics described above - // when we notice that this file definitely needs fixing - // (it mentions os.Error or something similar). - var fixed bool - var didHeuristic bool - heuristic := func() { - if didHeuristic { - return - } - didHeuristic = true - - // We have identified a necessary fix (like os.Error -> error) - // but have not applied it or any others yet. Prepare the file - // for fixing and apply heuristic fixes. - - // Rename error to error_ to make room for error. - fixed = renameTop(f, "error", "error_") || fixed - - // Use type checker to build list of error implementations. - typeof, assign := typecheck(errorTypeConfig, f) - - isError := map[string]bool{} - for _, val := range assign["os.Error"] { - t := typeof[val] - if strings.HasPrefix(t, "*") { - t = t[1:] - } - if t != "" && !strings.HasPrefix(t, "func(") { - isError[t] = true - } - } - - // We use both the type check results and the "Error" name heuristic - // to identify implementations of os.Error. - isErrorImpl := func(typ string) bool { - return isError[typ] || errType.MatchString(typ) - } - - isErrorVar := func(x ast.Expr) bool { - if typ := typeof[x]; typ != "" { - return isErrorImpl(typ) || typ == "os.Error" - } - if sel, ok := x.(*ast.SelectorExpr); ok { - return sel.Sel.Name == "Error" || sel.Sel.Name == "Err" - } - if id, ok := x.(*ast.Ident); ok { - return errVar.MatchString(id.Name) - } - return false - } - - walk(f, func(n interface{}) { - // In method declaration on error implementation type, - // rename String() to Error() and Error() to Err(). - fn, ok := n.(*ast.FuncDecl) - if ok && - fn.Recv != nil && - len(fn.Recv.List) == 1 && - isErrorImpl(typeName(fn.Recv.List[0].Type)) { - // Rename. - switch fn.Name.Name { - case "String": - fn.Name.Name = "Error" - fixed = true - case "Error": - fn.Name.Name = "Err" - fixed = true - } - return - } - - // In type definition of an error implementation type, - // rename Error field to Err to make room for method. - // Given type XxxError struct { ... Error T } rename field to Err. - d, ok := n.(*ast.GenDecl) - if ok { - for _, s := range d.Specs { - switch s := s.(type) { - case *ast.TypeSpec: - if isErrorImpl(typeName(s.Name)) { - st, ok := s.Type.(*ast.StructType) - if ok { - for _, f := range st.Fields.List { - for _, n := range f.Names { - if n.Name == "Error" { - n.Name = "Err" - fixed = true - } - } - } - } - } - } - } - } - - // For values that are an error implementation type, - // rename .Error to .Err and .String to .Error - sel, selok := n.(*ast.SelectorExpr) - if selok && isErrorImpl(typeof[sel.X]) { - switch sel.Sel.Name { - case "Error": - sel.Sel.Name = "Err" - fixed = true - case "String": - sel.Sel.Name = "Error" - fixed = true - } - } - - // Assume x.Err is an error value and rename .String to .Error - // Children have been processed so the rewrite from Error to Err - // has already happened there. - if selok { - if subsel, ok := sel.X.(*ast.SelectorExpr); ok && subsel.Sel.Name == "Err" && sel.Sel.Name == "String" { - sel.Sel.Name = "Error" - fixed = true - } - } - - // For values that are an error variable, rename .String to .Error. - if selok && isErrorVar(sel.X) && sel.Sel.Name == "String" { - sel.Sel.Name = "Error" - fixed = true - } - - // Rewrite composite literal of error type to turn Error: into Err:. - lit, ok := n.(*ast.CompositeLit) - if ok && isErrorImpl(typeof[lit]) { - for _, e := range lit.Elts { - if kv, ok := e.(*ast.KeyValueExpr); ok && isName(kv.Key, "Error") { - kv.Key.(*ast.Ident).Name = "Err" - fixed = true - } - } - } - - // Rename os.Waitmsg to exec.ExitError - // when used in a type assertion on an error. - ta, ok := n.(*ast.TypeAssertExpr) - if ok && isErrorVar(ta.X) && isPtrPkgDot(ta.Type, "os", "Waitmsg") { - addImport(f, "exec") - sel := ta.Type.(*ast.StarExpr).X.(*ast.SelectorExpr) - sel.X.(*ast.Ident).Name = "exec" - sel.Sel.Name = "ExitError" - fixed = true - } - - }) - } - - fix := func() { - if fixed { - return - } - fixed = true - heuristic() - } - - if force["error"] { - heuristic() - } - - walk(f, func(n interface{}) { - p, ok := n.(*ast.Expr) - if !ok { - return - } - sel, ok := (*p).(*ast.SelectorExpr) - if !ok { - return - } - switch { - case isPkgDot(sel, "os", "Error"): - fix() - *p = &ast.Ident{NamePos: sel.Pos(), Name: "error"} - case isPkgDot(sel, "os", "NewError"): - fix() - addImport(f, "errors") - sel.X.(*ast.Ident).Name = "errors" - sel.Sel.Name = "New" - case isPkgDot(sel, "os", "EOF"): - fix() - addImport(f, "io") - sel.X.(*ast.Ident).Name = "io" - } - }) - - if fixed && !usesImport(f, "os") { - deleteImport(f, "os") - } - - return fixed -} - -func typeName(typ ast.Expr) string { - if p, ok := typ.(*ast.StarExpr); ok { - typ = p.X - } - id, ok := typ.(*ast.Ident) - if ok { - return id.Name - } - sel, ok := typ.(*ast.SelectorExpr) - if ok { - return typeName(sel.X) + "." + sel.Sel.Name - } - return "" -} diff --git a/src/cmd/fix/error_test.go b/src/cmd/fix/error_test.go deleted file mode 100644 index 027eed24f..000000000 --- a/src/cmd/fix/error_test.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(errorTests, errorFn) -} - -var errorTests = []testCase{ - { - Name: "error.0", - In: `package main - -func error() {} - -var error int -`, - Out: `package main - -func error() {} - -var error int -`, - }, - { - Name: "error.1", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -func error() {} - -var error int - -func g() { - error := 1 - _ = error -} - -func h(os.Error) {} - -func i(...os.Error) {} -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -func error_() {} - -var error_ int - -func g() { - error := 1 - _ = error -} - -func h(error) {} - -func i(...error) {} -`, - }, - { - Name: "error.2", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -func g() string { - // these all convert because f is known - if err := f(); err != nil { - return err.String() - } - if err1 := f(); err1 != nil { - return err1.String() - } - if e := f(); e != nil { - return e.String() - } - if x := f(); x != nil { - return x.String() - } - - // only the error names (err, err1, e) convert; u is not known - if err := u(); err != nil { - return err.String() - } - if err1 := u(); err1 != nil { - return err1.String() - } - if e := u(); e != nil { - return e.String() - } - if x := u(); x != nil { - return x.String() - } - return "" -} - -type T int - -func (t T) String() string { return "t" } - -type PT int - -func (p *PT) String() string { return "pt" } - -type MyError int - -func (t MyError) String() string { return "myerror" } - -type PMyError int - -func (p *PMyError) String() string { return "pmyerror" } - -func error() {} - -var error int -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -func g() string { - // these all convert because f is known - if err := f(); err != nil { - return err.Error() - } - if err1 := f(); err1 != nil { - return err1.Error() - } - if e := f(); e != nil { - return e.Error() - } - if x := f(); x != nil { - return x.Error() - } - - // only the error names (err, err1, e) convert; u is not known - if err := u(); err != nil { - return err.Error() - } - if err1 := u(); err1 != nil { - return err1.Error() - } - if e := u(); e != nil { - return e.Error() - } - if x := u(); x != nil { - return x.String() - } - return "" -} - -type T int - -func (t T) String() string { return "t" } - -type PT int - -func (p *PT) String() string { return "pt" } - -type MyError int - -func (t MyError) Error() string { return "myerror" } - -type PMyError int - -func (p *PMyError) Error() string { return "pmyerror" } - -func error_() {} - -var error_ int -`, - }, - { - Name: "error.3", - In: `package main - -import "os" - -func f() os.Error { - return os.EOF -} - -type PathError struct { - Name string - Error os.Error -} - -func (p *PathError) String() string { - return p.Name + ": " + p.Error.String() -} - -func (p *PathError) Error1() string { - p = &PathError{Error: nil} - return fmt.Sprint(p.Name, ": ", p.Error) -} -`, - Out: `package main - -import "io" - -func f() error { - return io.EOF -} - -type PathError struct { - Name string - Err error -} - -func (p *PathError) Error() string { - return p.Name + ": " + p.Err.Error() -} - -func (p *PathError) Error1() string { - p = &PathError{Err: nil} - return fmt.Sprint(p.Name, ": ", p.Err) -} -`, - }, -} diff --git a/src/cmd/fix/filepath.go b/src/cmd/fix/filepath.go deleted file mode 100644 index f31226018..000000000 --- a/src/cmd/fix/filepath.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(filepathFix) -} - -var filepathFix = fix{ - "filepath", - "2011-06-26", - filepathFunc, - `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator). - -http://codereview.appspot.com/4527090 -`, -} - -func filepathFunc(f *ast.File) (fixed bool) { - if !imports(f, "path/filepath") { - return - } - - walk(f, func(n interface{}) { - e, ok := n.(*ast.Expr) - if !ok { - return - } - - var ident string - switch { - case isPkgDot(*e, "filepath", "SeparatorString"): - ident = "filepath.Separator" - case isPkgDot(*e, "filepath", "ListSeparatorString"): - ident = "filepath.ListSeparator" - default: - return - } - - // string(filepath.[List]Separator) - *e = &ast.CallExpr{ - Fun: ast.NewIdent("string"), - Args: []ast.Expr{ast.NewIdent(ident)}, - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/filepath_test.go b/src/cmd/fix/filepath_test.go deleted file mode 100644 index 37a2f5d9f..000000000 --- a/src/cmd/fix/filepath_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(filepathTests, filepathFunc) -} - -var filepathTests = []testCase{ - { - Name: "filepath.0", - In: `package main - -import ( - "path/filepath" -) - -var _ = filepath.SeparatorString -var _ = filepath.ListSeparatorString -`, - Out: `package main - -import ( - "path/filepath" -) - -var _ = string(filepath.Separator) -var _ = string(filepath.ListSeparator) -`, - }, -} diff --git a/src/cmd/fix/go1pkgrename.go b/src/cmd/fix/go1pkgrename.go deleted file mode 100644 index f701f62f0..000000000 --- a/src/cmd/fix/go1pkgrename.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "strings" -) - -func init() { - register(go1pkgrenameFix) -} - -var go1pkgrenameFix = fix{ - "go1rename", - "2011-11-08", - go1pkgrename, - `Rewrite imports for packages moved during transition to Go 1. - -http://codereview.appspot.com/5316078 -`, -} - -var go1PackageRenames = []struct{ old, new string }{ - {"asn1", "encoding/asn1"}, - {"big", "math/big"}, - {"cmath", "math/cmplx"}, - {"csv", "encoding/csv"}, - {"exec", "os/exec"}, - {"exp/template/html", "html/template"}, - {"gob", "encoding/gob"}, - {"http", "net/http"}, - {"http/cgi", "net/http/cgi"}, - {"http/fcgi", "net/http/fcgi"}, - {"http/httptest", "net/http/httptest"}, - {"http/pprof", "net/http/pprof"}, - {"json", "encoding/json"}, - {"mail", "net/mail"}, - {"rpc", "net/rpc"}, - {"rpc/jsonrpc", "net/rpc/jsonrpc"}, - {"scanner", "text/scanner"}, - {"smtp", "net/smtp"}, - {"syslog", "log/syslog"}, - {"tabwriter", "text/tabwriter"}, - {"template", "text/template"}, - {"template/parse", "text/template/parse"}, - {"rand", "math/rand"}, - {"url", "net/url"}, - {"utf16", "unicode/utf16"}, - {"utf8", "unicode/utf8"}, - {"xml", "encoding/xml"}, - - // go.crypto sub-repository - {"crypto/bcrypt", "code.google.com/p/go.crypto/bcrypt"}, - {"crypto/blowfish", "code.google.com/p/go.crypto/blowfish"}, - {"crypto/cast5", "code.google.com/p/go.crypto/cast5"}, - {"crypto/md4", "code.google.com/p/go.crypto/md4"}, - {"crypto/ocsp", "code.google.com/p/go.crypto/ocsp"}, - {"crypto/openpgp", "code.google.com/p/go.crypto/openpgp"}, - {"crypto/openpgp/armor", "code.google.com/p/go.crypto/openpgp/armor"}, - {"crypto/openpgp/elgamal", "code.google.com/p/go.crypto/openpgp/elgamal"}, - {"crypto/openpgp/errors", "code.google.com/p/go.crypto/openpgp/errors"}, - {"crypto/openpgp/packet", "code.google.com/p/go.crypto/openpgp/packet"}, - {"crypto/openpgp/s2k", "code.google.com/p/go.crypto/openpgp/s2k"}, - {"crypto/ripemd160", "code.google.com/p/go.crypto/ripemd160"}, - {"crypto/twofish", "code.google.com/p/go.crypto/twofish"}, - {"crypto/xtea", "code.google.com/p/go.crypto/xtea"}, - {"exp/ssh", "code.google.com/p/go.crypto/ssh"}, - - // go.image sub-repository - {"image/bmp", "code.google.com/p/go.image/bmp"}, - {"image/tiff", "code.google.com/p/go.image/tiff"}, - - // go.net sub-repository - {"net/dict", "code.google.com/p/go.net/dict"}, - {"net/websocket", "code.google.com/p/go.net/websocket"}, - {"exp/spdy", "code.google.com/p/go.net/spdy"}, - {"http/spdy", "code.google.com/p/go.net/spdy"}, - - // go.codereview sub-repository - {"encoding/git85", "code.google.com/p/go.codereview/git85"}, - {"patch", "code.google.com/p/go.codereview/patch"}, - - // exp - {"ebnf", "exp/ebnf"}, - {"go/types", "exp/types"}, - - // deleted - {"container/vector", ""}, - {"exp/datafmt", ""}, - {"go/typechecker", ""}, - {"old/netchan", ""}, - {"old/regexp", ""}, - {"old/template", ""}, - {"try", ""}, -} - -var go1PackageNameRenames = []struct{ newPath, old, new string }{ - {"html/template", "html", "template"}, - {"math/cmplx", "cmath", "cmplx"}, -} - -func go1pkgrename(f *ast.File) bool { - fixed := false - - // First update the imports. - for _, rename := range go1PackageRenames { - spec := importSpec(f, rename.old) - if spec == nil { - continue - } - if rename.new == "" { - warn(spec.Pos(), "package %q has been deleted in Go 1", rename.old) - continue - } - if rewriteImport(f, rename.old, rename.new) { - fixed = true - } - if strings.HasPrefix(rename.new, "exp/") { - warn(spec.Pos(), "package %q is not part of Go 1", rename.new) - } - } - if !fixed { - return false - } - - // Now update the package names used by importers. - for _, rename := range go1PackageNameRenames { - // These are rare packages, so do the import test before walking. - if imports(f, rename.newPath) { - walk(f, func(n interface{}) { - if sel, ok := n.(*ast.SelectorExpr); ok { - if isTopName(sel.X, rename.old) { - // We know Sel.X is an Ident. - sel.X.(*ast.Ident).Name = rename.new - return - } - } - }) - } - } - - return fixed -} diff --git a/src/cmd/fix/go1pkgrename_test.go b/src/cmd/fix/go1pkgrename_test.go deleted file mode 100644 index 840e443b0..000000000 --- a/src/cmd/fix/go1pkgrename_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(go1pkgrenameTests, go1pkgrename) -} - -var go1pkgrenameTests = []testCase{ - { - Name: "go1rename.0", - In: `package main - -import ( - "asn1" - "big" - "cmath" - "csv" - "exec" - "exp/template/html" - "gob" - "http" - "http/cgi" - "http/fcgi" - "http/httptest" - "http/pprof" - "json" - "mail" - "rand" - "rpc" - "rpc/jsonrpc" - "scanner" - "smtp" - "syslog" - "tabwriter" - "template" - "template/parse" - "url" - "utf16" - "utf8" - "xml" - - "crypto/bcrypt" -) -`, - Out: `package main - -import ( - "encoding/asn1" - "encoding/csv" - "encoding/gob" - "encoding/json" - "encoding/xml" - "html/template" - "log/syslog" - "math/big" - "math/cmplx" - "math/rand" - "net/http" - "net/http/cgi" - "net/http/fcgi" - "net/http/httptest" - "net/http/pprof" - "net/mail" - "net/rpc" - "net/rpc/jsonrpc" - "net/smtp" - "net/url" - "os/exec" - "text/scanner" - "text/tabwriter" - "text/template" - "text/template/parse" - "unicode/utf16" - "unicode/utf8" - - "code.google.com/p/go.crypto/bcrypt" -) -`, - }, - { - Name: "go1rename.1", - In: `package main - -import "cmath" -import poot "exp/template/html" - -import ( - "ebnf" - "old/regexp" -) - -var _ = cmath.Sin -var _ = poot.Poot -`, - Out: `package main - -import "math/cmplx" -import poot "html/template" - -import ( - "exp/ebnf" - "old/regexp" -) - -var _ = cmplx.Sin -var _ = poot.Poot -`, - }, - { - Name: "go1rename.2", - In: `package foo - -import ( - "fmt" - "http" - "url" - - "google/secret/project/go" -) - -func main() {} -`, - Out: `package foo - -import ( - "fmt" - "net/http" - "net/url" - - "google/secret/project/go" -) - -func main() {} -`, - }, -} diff --git a/src/cmd/fix/go1rename.go b/src/cmd/fix/go1rename.go deleted file mode 100644 index 9266c749c..000000000 --- a/src/cmd/fix/go1rename.go +++ /dev/null @@ -1,167 +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 main - -func init() { - register(go1renameFix) -} - -var go1renameFix = fix{ - "go1rename", - "2012-02-12", - renameFix(go1renameReplace), - `Rewrite package-level names that have been renamed in Go 1. - -http://codereview.appspot.com/5625045/ -http://codereview.appspot.com/5672072/ -`, -} - -var go1renameReplace = []rename{ - { - OldImport: "crypto/aes", - NewImport: "crypto/cipher", - Old: "*aes.Cipher", - New: "cipher.Block", - }, - { - OldImport: "crypto/des", - NewImport: "crypto/cipher", - Old: "*des.Cipher", - New: "cipher.Block", - }, - { - OldImport: "crypto/des", - NewImport: "crypto/cipher", - Old: "*des.TripleDESCipher", - New: "cipher.Block", - }, - { - OldImport: "encoding/json", - NewImport: "", - Old: "json.MarshalForHTML", - New: "json.Marshal", - }, - { - OldImport: "net/url", - NewImport: "", - Old: "url.ParseWithReference", - New: "url.Parse", - }, - { - OldImport: "net/url", - NewImport: "", - Old: "url.ParseRequest", - New: "url.ParseRequestURI", - }, - { - OldImport: "os", - NewImport: "syscall", - Old: "os.Exec", - New: "syscall.Exec", - }, - { - OldImport: "runtime", - NewImport: "", - Old: "runtime.Cgocalls", - New: "runtime.NumCgoCall", - }, - { - OldImport: "runtime", - NewImport: "", - Old: "runtime.Goroutines", - New: "runtime.NumGoroutine", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrPersistEOF", - New: "httputil.ErrPersistEOF", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrPipeline", - New: "httputil.ErrPipeline", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ErrClosed", - New: "httputil.ErrClosed", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ServerConn", - New: "httputil.ServerConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ClientConn", - New: "httputil.ClientConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewChunkedReader", - New: "httputil.NewChunkedReader", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewChunkedWriter", - New: "httputil.NewChunkedWriter", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.ReverseProxy", - New: "httputil.ReverseProxy", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewSingleHostReverseProxy", - New: "httputil.NewSingleHostReverseProxy", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpRequest", - New: "httputil.DumpRequest", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpRequestOut", - New: "httputil.DumpRequestOut", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.DumpResponse", - New: "httputil.DumpResponse", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewClientConn", - New: "httputil.NewClientConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewServerConn", - New: "httputil.NewServerConn", - }, - { - OldImport: "net/http", - NewImport: "net/http/httputil", - Old: "http.NewProxyClientConn", - New: "httputil.NewProxyClientConn", - }, -} diff --git a/src/cmd/fix/go1rename_test.go b/src/cmd/fix/go1rename_test.go deleted file mode 100644 index 90219ba71..000000000 --- a/src/cmd/fix/go1rename_test.go +++ /dev/null @@ -1,195 +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 main - -func init() { - addTestCases(go1renameTests, go1renameFix.f) -} - -var go1renameTests = []testCase{ - { - Name: "go1rename.0", - In: `package main - -import ( - "crypto/aes" - "crypto/des" - "encoding/json" - "net/http" - "net/url" - "os" - "runtime" -) - -var ( - _ *aes.Cipher - _ *des.Cipher - _ *des.TripleDESCipher - _ = json.MarshalForHTML - _ = aes.New() - _ = url.Parse - _ = url.ParseWithReference - _ = url.ParseRequest - _ = os.Exec - _ = runtime.Cgocalls - _ = runtime.Goroutines - _ = http.ErrPersistEOF - _ = http.ErrPipeline - _ = http.ErrClosed - _ = http.NewSingleHostReverseProxy - _ = http.NewChunkedReader - _ = http.NewChunkedWriter - _ *http.ReverseProxy - _ *http.ClientConn - _ *http.ServerConn -) -`, - Out: `package main - -import ( - "crypto/aes" - "crypto/cipher" - "encoding/json" - "net/http/httputil" - "net/url" - "runtime" - "syscall" -) - -var ( - _ cipher.Block - _ cipher.Block - _ cipher.Block - _ = json.Marshal - _ = aes.New() - _ = url.Parse - _ = url.Parse - _ = url.ParseRequestURI - _ = syscall.Exec - _ = runtime.NumCgoCall - _ = runtime.NumGoroutine - _ = httputil.ErrPersistEOF - _ = httputil.ErrPipeline - _ = httputil.ErrClosed - _ = httputil.NewSingleHostReverseProxy - _ = httputil.NewChunkedReader - _ = httputil.NewChunkedWriter - _ *httputil.ReverseProxy - _ *httputil.ClientConn - _ *httputil.ServerConn -) -`, - }, - { - Name: "httputil.0", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) -} -`, - Out: `package main - -import "net/http/httputil" - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) -} -`, - }, - { - Name: "httputil.1", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) -} -`, - Out: `package main - -import "net/http/httputil" - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) -} -`, - }, - { - Name: "httputil.2", - In: `package main - -import "net/http" - -func f() { - http.DumpRequest(nil, false) - http.DumpRequestOut(nil, false) - http.DumpResponse(nil, false) - http.NewChunkedReader(nil) - http.NewChunkedWriter(nil) - http.NewClientConn(nil, nil) - http.NewProxyClientConn(nil, nil) - http.NewServerConn(nil, nil) - http.NewSingleHostReverseProxy(nil) - http.Get("") -} -`, - Out: `package main - -import ( - "net/http" - "net/http/httputil" -) - -func f() { - httputil.DumpRequest(nil, false) - httputil.DumpRequestOut(nil, false) - httputil.DumpResponse(nil, false) - httputil.NewChunkedReader(nil) - httputil.NewChunkedWriter(nil) - httputil.NewClientConn(nil, nil) - httputil.NewProxyClientConn(nil, nil) - httputil.NewServerConn(nil, nil) - httputil.NewSingleHostReverseProxy(nil) - http.Get("") -} -`, - }, -} diff --git a/src/cmd/fix/googlecode.go b/src/cmd/fix/googlecode.go deleted file mode 100644 index 143781a74..000000000 --- a/src/cmd/fix/googlecode.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "regexp" -) - -func init() { - register(googlecodeFix) -} - -var googlecodeFix = fix{ - "googlecode", - "2011-11-21", - googlecode, - `Rewrite Google Code imports from the deprecated form -"foo.googlecode.com/vcs/path" to "code.google.com/p/foo/path". -`, -} - -var googlecodeRe = regexp.MustCompile(`^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$`) - -func googlecode(f *ast.File) bool { - fixed := false - - for _, s := range f.Imports { - old := importPath(s) - if m := googlecodeRe.FindStringSubmatch(old); m != nil { - new := "code.google.com/p/" + m[1] + m[3] - if rewriteImport(f, old, new) { - fixed = true - } - } - } - - return fixed -} diff --git a/src/cmd/fix/googlecode_test.go b/src/cmd/fix/googlecode_test.go deleted file mode 100644 index c62ee4f32..000000000 --- a/src/cmd/fix/googlecode_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(googlecodeTests, googlecode) -} - -var googlecodeTests = []testCase{ - { - Name: "googlecode.0", - In: `package main - -import ( - "foo.googlecode.com/hg/bar" - "go-qux-23.googlecode.com/svn" - "zap.googlecode.com/git/some/path" -) -`, - Out: `package main - -import ( - "code.google.com/p/foo/bar" - "code.google.com/p/go-qux-23" - "code.google.com/p/zap/some/path" -) -`, - }, -} diff --git a/src/cmd/fix/hashsum.go b/src/cmd/fix/hashsum.go deleted file mode 100644 index 0df6ad749..000000000 --- a/src/cmd/fix/hashsum.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(hashSumFix) -} - -var hashSumFix = fix{ - "hashsum", - "2011-11-30", - hashSumFn, - `Pass a nil argument to calls to hash.Sum - -This fix rewrites code so that it passes a nil argument to hash.Sum. -The additional argument will allow callers to avoid an -allocation in the future. - -http://codereview.appspot.com/5448065 -`, -} - -// Type-checking configuration: tell the type-checker this basic -// information about types, functions, and variables in external packages. -var hashSumTypeConfig = &TypeConfig{ - Var: map[string]string{ - "crypto.MD4": "crypto.Hash", - "crypto.MD5": "crypto.Hash", - "crypto.SHA1": "crypto.Hash", - "crypto.SHA224": "crypto.Hash", - "crypto.SHA256": "crypto.Hash", - "crypto.SHA384": "crypto.Hash", - "crypto.SHA512": "crypto.Hash", - "crypto.MD5SHA1": "crypto.Hash", - "crypto.RIPEMD160": "crypto.Hash", - }, - - Func: map[string]string{ - "adler32.New": "hash.Hash", - "crc32.New": "hash.Hash", - "crc32.NewIEEE": "hash.Hash", - "crc64.New": "hash.Hash", - "fnv.New32a": "hash.Hash", - "fnv.New32": "hash.Hash", - "fnv.New64a": "hash.Hash", - "fnv.New64": "hash.Hash", - "hmac.New": "hash.Hash", - "hmac.NewMD5": "hash.Hash", - "hmac.NewSHA1": "hash.Hash", - "hmac.NewSHA256": "hash.Hash", - "md4.New": "hash.Hash", - "md5.New": "hash.Hash", - "ripemd160.New": "hash.Hash", - "sha1.New224": "hash.Hash", - "sha1.New": "hash.Hash", - "sha256.New224": "hash.Hash", - "sha256.New": "hash.Hash", - "sha512.New384": "hash.Hash", - "sha512.New": "hash.Hash", - }, - - Type: map[string]*Type{ - "crypto.Hash": { - Method: map[string]string{ - "New": "func() hash.Hash", - }, - }, - }, -} - -func hashSumFn(f *ast.File) bool { - typeof, _ := typecheck(hashSumTypeConfig, f) - - fixed := false - - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if ok && len(call.Args) == 0 { - sel, ok := call.Fun.(*ast.SelectorExpr) - if ok && sel.Sel.Name == "Sum" && typeof[sel.X] == "hash.Hash" { - call.Args = append(call.Args, ast.NewIdent("nil")) - fixed = true - } - } - }) - - return fixed -} diff --git a/src/cmd/fix/hashsum_test.go b/src/cmd/fix/hashsum_test.go deleted file mode 100644 index 241af2020..000000000 --- a/src/cmd/fix/hashsum_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(hashSumTests, hashSumFn) -} - -var hashSumTests = []testCase{ - { - Name: "hashsum.0", - In: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - return h.Sum() -} -`, - Out: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - return h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.1", - In: `package main - -func f(h hash.Hash) []byte { - return h.Sum() -} -`, - Out: `package main - -func f(h hash.Hash) []byte { - return h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.0", - In: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - h.Write([]byte("foo")) - digest := h.Sum() -} -`, - Out: `package main - -import "crypto/sha256" - -func f() []byte { - h := sha256.New() - h.Write([]byte("foo")) - digest := h.Sum(nil) -} -`, - }, - - { - Name: "hashsum.0", - In: `package main - -import _ "crypto/sha256" -import "crypto" - -func f() []byte { - hashType := crypto.SHA256 - h := hashType.New() - digest := h.Sum() -} -`, - Out: `package main - -import _ "crypto/sha256" -import "crypto" - -func f() []byte { - hashType := crypto.SHA256 - h := hashType.New() - digest := h.Sum(nil) -} -`, - }, -} diff --git a/src/cmd/fix/hmacnew.go b/src/cmd/fix/hmacnew.go deleted file mode 100644 index c0c44ef3e..000000000 --- a/src/cmd/fix/hmacnew.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(hmacNewFix) -} - -var hmacNewFix = fix{ - "hmacnew", - "2012-01-19", - hmacnew, - `Deprecate hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256. - -This fix rewrites code using hmac.NewMD5, hmac.NewSHA1 and hmac.NewSHA256 to -use hmac.New: - - hmac.NewMD5(key) -> hmac.New(md5.New, key) - hmac.NewSHA1(key) -> hmac.New(sha1.New, key) - hmac.NewSHA256(key) -> hmac.New(sha256.New, key) - -`, -} - -func hmacnew(f *ast.File) (fixed bool) { - if !imports(f, "crypto/hmac") { - return - } - - walk(f, func(n interface{}) { - ce, ok := n.(*ast.CallExpr) - if !ok { - return - } - - var pkg string - switch { - case isPkgDot(ce.Fun, "hmac", "NewMD5"): - pkg = "md5" - case isPkgDot(ce.Fun, "hmac", "NewSHA1"): - pkg = "sha1" - case isPkgDot(ce.Fun, "hmac", "NewSHA256"): - pkg = "sha256" - default: - return - } - - addImport(f, "crypto/"+pkg) - - ce.Fun = ast.NewIdent("hmac.New") - ce.Args = append([]ast.Expr{ast.NewIdent(pkg + ".New")}, ce.Args...) - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/hmacnew_test.go b/src/cmd/fix/hmacnew_test.go deleted file mode 100644 index 5aeee8573..000000000 --- a/src/cmd/fix/hmacnew_test.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(hmacNewTests, hmacnew) -} - -var hmacNewTests = []testCase{ - { - Name: "hmacnew.0", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewSHA1([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.1", - In: `package main - -import "crypto/hmac" - -var key = make([]byte, 8) -var f = hmac.NewSHA1(key) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var key = make([]byte, 8) -var f = hmac.New(sha1.New, key) -`, - }, - { - Name: "hmacnew.2", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewMD5([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/md5" -) - -var f = hmac.New(md5.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.3", - In: `package main - -import "crypto/hmac" - -var f = hmac.NewSHA256([]byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha256" -) - -var f = hmac.New(sha256.New, []byte("some key")) -`, - }, - { - Name: "hmacnew.4", - In: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - Out: `package main - -import ( - "crypto/hmac" - "crypto/sha1" -) - -var f = hmac.New(sha1.New, []byte("some key")) -`, - }, -} diff --git a/src/cmd/fix/htmlerr.go b/src/cmd/fix/htmlerr.go deleted file mode 100644 index b5105c822..000000000 --- a/src/cmd/fix/htmlerr.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(htmlerrFix) -} - -var htmlerrFix = fix{ - "htmlerr", - "2011-11-04", - htmlerr, - `Rename html's Tokenizer.Error method to Err. - -http://codereview.appspot.com/5327064/ -`, -} - -var htmlerrTypeConfig = &TypeConfig{ - Func: map[string]string{ - "html.NewTokenizer": "html.Tokenizer", - }, -} - -func htmlerr(f *ast.File) bool { - if !imports(f, "html") { - return false - } - - typeof, _ := typecheck(htmlerrTypeConfig, f) - - fixed := false - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if ok && typeof[s.X] == "html.Tokenizer" && s.Sel.Name == "Error" { - s.Sel.Name = "Err" - fixed = true - } - }) - return fixed -} diff --git a/src/cmd/fix/htmlerr_test.go b/src/cmd/fix/htmlerr_test.go deleted file mode 100644 index 043abc42a..000000000 --- a/src/cmd/fix/htmlerr_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(htmlerrTests, htmlerr) -} - -var htmlerrTests = []testCase{ - { - Name: "htmlerr.0", - In: `package main - -import ( - "html" -) - -func f() { - e := errors.New("") - t := html.NewTokenizer(r) - _, _ = e.Error(), t.Error() -} -`, - Out: `package main - -import ( - "html" -) - -func f() { - e := errors.New("") - t := html.NewTokenizer(r) - _, _ = e.Error(), t.Err() -} -`, - }, -} diff --git a/src/cmd/fix/httpfinalurl.go b/src/cmd/fix/httpfinalurl.go deleted file mode 100644 index 49b9f1c51..000000000 --- a/src/cmd/fix/httpfinalurl.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(httpFinalURLFix) -} - -var httpFinalURLFix = fix{ - "httpfinalurl", - "2011-05-13", - httpfinalurl, - `Adapt http Get calls to not have a finalURL result parameter. - -http://codereview.appspot.com/4535056/ -`, -} - -func httpfinalurl(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // Fix up calls to http.Get. - // - // If they have blank identifiers, remove them: - // resp, _, err := http.Get(url) - // -> resp, err := http.Get(url) - // - // But if they're using the finalURL parameter, warn: - // resp, finalURL, err := http.Get(url) - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 { - return - } - - if !isCall(as.Rhs[0], "http", "Get") { - return - } - - if isBlank(as.Lhs[1]) { - as.Lhs = []ast.Expr{as.Lhs[0], as.Lhs[2]} - fixed = true - } else { - warn(as.Pos(), "call to http.Get records final URL") - } - }) - return fixed -} diff --git a/src/cmd/fix/httpfinalurl_test.go b/src/cmd/fix/httpfinalurl_test.go deleted file mode 100644 index 9249f7e18..000000000 --- a/src/cmd/fix/httpfinalurl_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpfinalurlTests, httpfinalurl) -} - -var httpfinalurlTests = []testCase{ - { - Name: "finalurl.0", - In: `package main - -import ( - "http" -) - -func f() { - resp, _, err := http.Get("http://www.google.com/") - _, _ = resp, err -} -`, - Out: `package main - -import ( - "http" -) - -func f() { - resp, err := http.Get("http://www.google.com/") - _, _ = resp, err -} -`, - }, -} diff --git a/src/cmd/fix/httpfs.go b/src/cmd/fix/httpfs.go deleted file mode 100644 index d87b30f9d..000000000 --- a/src/cmd/fix/httpfs.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(httpFileSystemFix) -} - -var httpFileSystemFix = fix{ - "httpfs", - "2011-06-27", - httpfs, - `Adapt http FileServer to take a FileSystem. - -http://codereview.appspot.com/4629047 http FileSystem interface -`, -} - -func httpfs(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "http", "FileServer") { - return - } - if len(call.Args) != 2 { - return - } - dir, prefix := call.Args[0], call.Args[1] - call.Args = []ast.Expr{&ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("http"), - Sel: ast.NewIdent("Dir"), - }, - Args: []ast.Expr{dir}, - }} - wrapInStripHandler := true - if prefixLit, ok := prefix.(*ast.BasicLit); ok { - if prefixLit.Kind == token.STRING && (prefixLit.Value == `"/"` || prefixLit.Value == `""`) { - wrapInStripHandler = false - } - } - if wrapInStripHandler { - call.Fun.(*ast.SelectorExpr).Sel = ast.NewIdent("StripPrefix") - call.Args = []ast.Expr{ - prefix, - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("http"), - Sel: ast.NewIdent("FileServer"), - }, - Args: call.Args, - }, - } - } - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/httpfs_test.go b/src/cmd/fix/httpfs_test.go deleted file mode 100644 index dd8ef2cfd..000000000 --- a/src/cmd/fix/httpfs_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpFileSystemTests, httpfs) -} - -var httpFileSystemTests = []testCase{ - { - Name: "httpfs.0", - In: `package httpfs - -import ( - "http" -) - -func f() { - _ = http.FileServer("/var/www/foo", "/") - _ = http.FileServer("/var/www/foo", "") - _ = http.FileServer("/var/www/foo/bar", "/bar") - s := "/foo" - _ = http.FileServer(s, "/") - prefix := "/p" - _ = http.FileServer(s, prefix) -} -`, - Out: `package httpfs - -import ( - "http" -) - -func f() { - _ = http.FileServer(http.Dir("/var/www/foo")) - _ = http.FileServer(http.Dir("/var/www/foo")) - _ = http.StripPrefix("/bar", http.FileServer(http.Dir("/var/www/foo/bar"))) - s := "/foo" - _ = http.FileServer(http.Dir(s)) - prefix := "/p" - _ = http.StripPrefix(prefix, http.FileServer(http.Dir(s))) -} -`, - }, -} diff --git a/src/cmd/fix/httpheaders.go b/src/cmd/fix/httpheaders.go deleted file mode 100644 index 15c21ac86..000000000 --- a/src/cmd/fix/httpheaders.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(httpHeadersFix) -} - -var httpHeadersFix = fix{ - "httpheaders", - "2011-06-16", - httpheaders, - `Rename http Referer, UserAgent, Cookie, SetCookie, which are now methods. - -http://codereview.appspot.com/4620049/ -`, -} - -func httpheaders(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - called := make(map[ast.Node]bool) - walk(f, func(ni interface{}) { - switch n := ni.(type) { - case *ast.CallExpr: - called[n.Fun] = true - } - }) - - fixed := false - typeof, _ := typecheck(headerTypeConfig, f) - walk(f, func(ni interface{}) { - switch n := ni.(type) { - case *ast.SelectorExpr: - if called[n] { - break - } - if t := typeof[n.X]; t != "*http.Request" && t != "*http.Response" { - break - } - switch n.Sel.Name { - case "Referer", "UserAgent": - n.Sel.Name += "()" - fixed = true - case "Cookie": - n.Sel.Name = "Cookies()" - fixed = true - } - } - }) - return fixed -} - -var headerTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "*http.Request": {}, - "*http.Response": {}, - }, -} diff --git a/src/cmd/fix/httpheaders_test.go b/src/cmd/fix/httpheaders_test.go deleted file mode 100644 index 37506b82d..000000000 --- a/src/cmd/fix/httpheaders_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpHeadersTests, httpheaders) -} - -var httpHeadersTests = []testCase{ - { - Name: "httpheaders.0", - In: `package headertest - -import ( - "http" -) - -type Other struct { - Referer string - UserAgent string - Cookie []*http.Cookie -} - -func f(req *http.Request, res *http.Response, other *Other) { - _ = req.Referer - _ = req.UserAgent - _ = req.Cookie - - _ = res.Cookie - - _ = other.Referer - _ = other.UserAgent - _ = other.Cookie - - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - _ = res.Cookies() -} -`, - Out: `package headertest - -import ( - "http" -) - -type Other struct { - Referer string - UserAgent string - Cookie []*http.Cookie -} - -func f(req *http.Request, res *http.Response, other *Other) { - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - - _ = res.Cookies() - - _ = other.Referer - _ = other.UserAgent - _ = other.Cookie - - _ = req.Referer() - _ = req.UserAgent() - _ = req.Cookies() - _ = res.Cookies() -} -`, - }, -} diff --git a/src/cmd/fix/httpserver.go b/src/cmd/fix/httpserver.go deleted file mode 100644 index 7aa651786..000000000 --- a/src/cmd/fix/httpserver.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(httpserverFix) -} - -var httpserverFix = fix{ - "httpserver", - "2011-03-15", - httpserver, - `Adapt http server methods and functions to changes -made to the http ResponseWriter interface. - -http://codereview.appspot.com/4245064 Hijacker -http://codereview.appspot.com/4239076 Header -http://codereview.appspot.com/4239077 Flusher -http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS -`, -} - -func httpserver(f *ast.File) bool { - if !imports(f, "http") { - return false - } - - fixed := false - for _, decl := range f.Decls { - fn, ok := decl.(*ast.FuncDecl) - if !ok { - continue - } - w, req, ok := isServeHTTP(fn) - if !ok { - continue - } - walk(fn.Body, func(n interface{}) { - // Want to replace expression sometimes, - // so record pointer to it for updating below. - ptr, ok := n.(*ast.Expr) - if ok { - n = *ptr - } - - // Look for w.UsingTLS() and w.Remoteaddr(). - call, ok := n.(*ast.CallExpr) - if !ok || (len(call.Args) != 0 && len(call.Args) != 2) { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - if !refersTo(sel.X, w) { - return - } - switch sel.Sel.String() { - case "Hijack": - // replace w with w.(http.Hijacker) - sel.X = &ast.TypeAssertExpr{ - X: sel.X, - Type: ast.NewIdent("http.Hijacker"), - } - fixed = true - case "Flush": - // replace w with w.(http.Flusher) - sel.X = &ast.TypeAssertExpr{ - X: sel.X, - Type: ast.NewIdent("http.Flusher"), - } - fixed = true - case "UsingTLS": - if ptr == nil { - // can only replace expression if we have pointer to it - break - } - // replace with req.TLS != nil - *ptr = &ast.BinaryExpr{ - X: &ast.SelectorExpr{ - X: ast.NewIdent(req.String()), - Sel: ast.NewIdent("TLS"), - }, - Op: token.NEQ, - Y: ast.NewIdent("nil"), - } - fixed = true - case "RemoteAddr": - if ptr == nil { - // can only replace expression if we have pointer to it - break - } - // replace with req.RemoteAddr - *ptr = &ast.SelectorExpr{ - X: ast.NewIdent(req.String()), - Sel: ast.NewIdent("RemoteAddr"), - } - fixed = true - case "SetHeader": - // replace w.SetHeader with w.Header().Set - // or w.Header().Del if second argument is "" - sel.X = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent(w.String()), - Sel: ast.NewIdent("Header"), - }, - } - sel.Sel = ast.NewIdent("Set") - if len(call.Args) == 2 && isEmptyString(call.Args[1]) { - sel.Sel = ast.NewIdent("Del") - call.Args = call.Args[:1] - } - fixed = true - } - }) - } - return fixed -} - -func isServeHTTP(fn *ast.FuncDecl) (w, req *ast.Ident, ok bool) { - for _, field := range fn.Type.Params.List { - if isPkgDot(field.Type, "http", "ResponseWriter") { - w = field.Names[0] - continue - } - if isPtrPkgDot(field.Type, "http", "Request") { - req = field.Names[0] - continue - } - } - - ok = w != nil && req != nil - return -} diff --git a/src/cmd/fix/httpserver_test.go b/src/cmd/fix/httpserver_test.go deleted file mode 100644 index b6ddff27e..000000000 --- a/src/cmd/fix/httpserver_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(httpserverTests, httpserver) -} - -var httpserverTests = []testCase{ - { - Name: "httpserver.0", - In: `package main - -import "http" - -func f(xyz http.ResponseWriter, abc *http.Request, b string) { - xyz.SetHeader("foo", "bar") - xyz.SetHeader("baz", "") - xyz.Hijack() - xyz.Flush() - go xyz.Hijack() - defer xyz.Flush() - _ = xyz.UsingTLS() - _ = true == xyz.UsingTLS() - _ = xyz.RemoteAddr() - _ = xyz.RemoteAddr() == "hello" - if xyz.UsingTLS() { - } -} -`, - Out: `package main - -import "http" - -func f(xyz http.ResponseWriter, abc *http.Request, b string) { - xyz.Header().Set("foo", "bar") - xyz.Header().Del("baz") - xyz.(http.Hijacker).Hijack() - xyz.(http.Flusher).Flush() - go xyz.(http.Hijacker).Hijack() - defer xyz.(http.Flusher).Flush() - _ = abc.TLS != nil - _ = true == (abc.TLS != nil) - _ = abc.RemoteAddr - _ = abc.RemoteAddr == "hello" - if abc.TLS != nil { - } -} -`, - }, -} diff --git a/src/cmd/fix/imagecolor.go b/src/cmd/fix/imagecolor.go deleted file mode 100644 index 1aac40a6f..000000000 --- a/src/cmd/fix/imagecolor.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imagecolorFix) -} - -var imagecolorFix = fix{ - "imagecolor", - "2011-10-04", - imagecolor, - `Adapt code to types moved from image to color. - -http://codereview.appspot.com/5132048 -`, -} - -var colorRenames = []struct{ in, out string }{ - {"Color", "Color"}, - {"ColorModel", "Model"}, - {"ColorModelFunc", "ModelFunc"}, - {"PalettedColorModel", "Palette"}, - - {"RGBAColor", "RGBA"}, - {"RGBA64Color", "RGBA64"}, - {"NRGBAColor", "NRGBA"}, - {"NRGBA64Color", "NRGBA64"}, - {"AlphaColor", "Alpha"}, - {"Alpha16Color", "Alpha16"}, - {"GrayColor", "Gray"}, - {"Gray16Color", "Gray16"}, - - {"RGBAColorModel", "RGBAModel"}, - {"RGBA64ColorModel", "RGBA64Model"}, - {"NRGBAColorModel", "NRGBAModel"}, - {"NRGBA64ColorModel", "NRGBA64Model"}, - {"AlphaColorModel", "AlphaModel"}, - {"Alpha16ColorModel", "Alpha16Model"}, - {"GrayColorModel", "GrayModel"}, - {"Gray16ColorModel", "Gray16Model"}, -} - -func imagecolor(f *ast.File) (fixed bool) { - if !imports(f, "image") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "image") { - return - } - - switch sel := s.Sel.String(); { - case sel == "ColorImage": - s.Sel = &ast.Ident{Name: "Uniform"} - fixed = true - case sel == "NewColorImage": - s.Sel = &ast.Ident{Name: "NewUniform"} - fixed = true - default: - for _, rename := range colorRenames { - if sel == rename.in { - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = rename.out - fixed = true - } - } - } - }) - - if fixed && !usesImport(f, "image") { - deleteImport(f, "image") - } - return -} diff --git a/src/cmd/fix/imagecolor_test.go b/src/cmd/fix/imagecolor_test.go deleted file mode 100644 index c62365481..000000000 --- a/src/cmd/fix/imagecolor_test.go +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(colorTests, imagecolor) -} - -var colorTests = []testCase{ - { - Name: "color.0", - In: `package main - -import ( - "image" -) - -var ( - _ image.Image - _ image.RGBA - _ image.Black - _ image.Color - _ image.ColorModel - _ image.ColorModelFunc - _ image.PalettedColorModel - _ image.RGBAColor - _ image.RGBA64Color - _ image.NRGBAColor - _ image.NRGBA64Color - _ image.AlphaColor - _ image.Alpha16Color - _ image.GrayColor - _ image.Gray16Color -) - -func f() { - _ = image.RGBAColorModel - _ = image.RGBA64ColorModel - _ = image.NRGBAColorModel - _ = image.NRGBA64ColorModel - _ = image.AlphaColorModel - _ = image.Alpha16ColorModel - _ = image.GrayColorModel - _ = image.Gray16ColorModel -} -`, - Out: `package main - -import ( - "image" - "image/color" -) - -var ( - _ image.Image - _ image.RGBA - _ image.Black - _ color.Color - _ color.Model - _ color.ModelFunc - _ color.Palette - _ color.RGBA - _ color.RGBA64 - _ color.NRGBA - _ color.NRGBA64 - _ color.Alpha - _ color.Alpha16 - _ color.Gray - _ color.Gray16 -) - -func f() { - _ = color.RGBAModel - _ = color.RGBA64Model - _ = color.NRGBAModel - _ = color.NRGBA64Model - _ = color.AlphaModel - _ = color.Alpha16Model - _ = color.GrayModel - _ = color.Gray16Model -} -`, - }, - { - Name: "color.1", - In: `package main - -import ( - "fmt" - "image" -) - -func f() { - fmt.Println(image.RGBAColor{1, 2, 3, 4}.RGBA()) -} -`, - Out: `package main - -import ( - "fmt" - "image/color" -) - -func f() { - fmt.Println(color.RGBA{1, 2, 3, 4}.RGBA()) -} -`, - }, - { - Name: "color.2", - In: `package main - -import "image" - -var c *image.ColorImage = image.NewColorImage(nil) -`, - Out: `package main - -import "image" - -var c *image.Uniform = image.NewUniform(nil) -`, - }, -} diff --git a/src/cmd/fix/imagenew.go b/src/cmd/fix/imagenew.go deleted file mode 100644 index b4e36d4f0..000000000 --- a/src/cmd/fix/imagenew.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imagenewFix) -} - -var imagenewFix = fix{ - "imagenew", - "2011-09-14", - imagenew, - `Adapt image.NewXxx calls to pass an image.Rectangle instead of (w, h int). - -http://codereview.appspot.com/4964073 -`, -} - -var imagenewFuncs = map[string]bool{ - "NewRGBA": true, - "NewRGBA64": true, - "NewNRGBA": true, - "NewNRGBA64": true, - "NewAlpha": true, - "NewAlpha16": true, - "NewGray": true, - "NewGray16": true, -} - -func imagenew(f *ast.File) bool { - if !imports(f, "image") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok { - return - } - isNewFunc := false - for newFunc := range imagenewFuncs { - if len(call.Args) == 2 && isPkgDot(call.Fun, "image", newFunc) { - isNewFunc = true - break - } - } - if len(call.Args) == 3 && isPkgDot(call.Fun, "image", "NewPaletted") { - isNewFunc = true - } - if !isNewFunc { - return - } - // Replace image.NewXxx(w, h) with image.NewXxx(image.Rect(0, 0, w, h)). - rectArgs := []ast.Expr{ - &ast.BasicLit{Value: "0"}, - &ast.BasicLit{Value: "0"}, - } - rectArgs = append(rectArgs, call.Args[:2]...) - rect := []ast.Expr{ - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: &ast.Ident{ - Name: "image", - }, - Sel: &ast.Ident{ - Name: "Rect", - }, - }, - Args: rectArgs, - }, - } - call.Args = append(rect, call.Args[2:]...) - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/imagenew_test.go b/src/cmd/fix/imagenew_test.go deleted file mode 100644 index 30abed23c..000000000 --- a/src/cmd/fix/imagenew_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(imagenewTests, imagenew) -} - -var imagenewTests = []testCase{ - { - Name: "imagenew.0", - In: `package main - -import ( - "image" -) - -func f() { - image.NewRGBA(1, 2) - image.NewRGBA64(1, 2) - image.NewNRGBA(1, 2) - image.NewNRGBA64(1, 2) - image.NewAlpha(1, 2) - image.NewAlpha16(1, 2) - image.NewGray(1, 2) - image.NewGray16(1, 2) - image.NewPaletted(1, 2, nil) -} -`, - Out: `package main - -import ( - "image" -) - -func f() { - image.NewRGBA(image.Rect(0, 0, 1, 2)) - image.NewRGBA64(image.Rect(0, 0, 1, 2)) - image.NewNRGBA(image.Rect(0, 0, 1, 2)) - image.NewNRGBA64(image.Rect(0, 0, 1, 2)) - image.NewAlpha(image.Rect(0, 0, 1, 2)) - image.NewAlpha16(image.Rect(0, 0, 1, 2)) - image.NewGray(image.Rect(0, 0, 1, 2)) - image.NewGray16(image.Rect(0, 0, 1, 2)) - image.NewPaletted(image.Rect(0, 0, 1, 2), nil) -} -`, - }, -} diff --git a/src/cmd/fix/imageycbcr.go b/src/cmd/fix/imageycbcr.go deleted file mode 100644 index 41b96d18d..000000000 --- a/src/cmd/fix/imageycbcr.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(imageycbcrFix) -} - -var imageycbcrFix = fix{ - "imageycbcr", - "2011-12-20", - imageycbcr, - `Adapt code to types moved from image/ycbcr to image and image/color. - -http://codereview.appspot.com/5493084 -`, -} - -func imageycbcr(f *ast.File) (fixed bool) { - if !imports(f, "image/ycbcr") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "ycbcr") { - return - } - - switch s.Sel.String() { - case "RGBToYCbCr", "YCbCrToRGB": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - case "YCbCrColor": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = "YCbCr" - case "YCbCrColorModel": - addImport(f, "image/color") - s.X.(*ast.Ident).Name = "color" - s.Sel.Name = "YCbCrModel" - case "SubsampleRatio", "SubsampleRatio444", "SubsampleRatio422", "SubsampleRatio420": - addImport(f, "image") - s.X.(*ast.Ident).Name = "image" - s.Sel.Name = "YCbCr" + s.Sel.Name - case "YCbCr": - addImport(f, "image") - s.X.(*ast.Ident).Name = "image" - default: - return - } - fixed = true - }) - - deleteImport(f, "image/ycbcr") - return -} diff --git a/src/cmd/fix/imageycbcr_test.go b/src/cmd/fix/imageycbcr_test.go deleted file mode 100644 index 23b599dcd..000000000 --- a/src/cmd/fix/imageycbcr_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(ycbcrTests, imageycbcr) -} - -var ycbcrTests = []testCase{ - { - Name: "ycbcr.0", - In: `package main - -import ( - "image/ycbcr" -) - -func f() { - _ = ycbcr.RGBToYCbCr - _ = ycbcr.YCbCrToRGB - _ = ycbcr.YCbCrColorModel - var _ ycbcr.YCbCrColor - var _ ycbcr.YCbCr - var ( - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio444 - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio422 - _ ycbcr.SubsampleRatio = ycbcr.SubsampleRatio420 - ) -} -`, - Out: `package main - -import ( - "image" - "image/color" -) - -func f() { - _ = color.RGBToYCbCr - _ = color.YCbCrToRGB - _ = color.YCbCrModel - var _ color.YCbCr - var _ image.YCbCr - var ( - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio444 - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio422 - _ image.YCbCrSubsampleRatio = image.YCbCrSubsampleRatio420 - ) -} -`, - }, -} diff --git a/src/cmd/fix/iocopyn.go b/src/cmd/fix/iocopyn.go deleted file mode 100644 index 720f3c689..000000000 --- a/src/cmd/fix/iocopyn.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(ioCopyNFix) -} - -var ioCopyNFix = fix{ - "iocopyn", - "2011-09-30", - ioCopyN, - `Rename io.Copyn to io.CopyN. - -http://codereview.appspot.com/5157045 -`, -} - -func ioCopyN(f *ast.File) bool { - if !imports(f, "io") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - if expr, ok := n.(ast.Expr); ok { - if isPkgDot(expr, "io", "Copyn") { - expr.(*ast.SelectorExpr).Sel.Name = "CopyN" - fixed = true - return - } - } - }) - return fixed -} diff --git a/src/cmd/fix/iocopyn_test.go b/src/cmd/fix/iocopyn_test.go deleted file mode 100644 index f86fad763..000000000 --- a/src/cmd/fix/iocopyn_test.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(ioCopyNTests, ioCopyN) -} - -var ioCopyNTests = []testCase{ - { - Name: "io.CopyN.0", - In: `package main - -import ( - "io" -) - -func f() { - io.Copyn(dst, src) - foo.Copyn(dst, src) -} -`, - Out: `package main - -import ( - "io" -) - -func f() { - io.CopyN(dst, src) - foo.Copyn(dst, src) -} -`, - }, -} diff --git a/src/cmd/fix/main.go b/src/cmd/fix/main.go index b151408d7..dc10d6beb 100644 --- a/src/cmd/fix/main.go +++ b/src/cmd/fix/main.go @@ -9,8 +9,8 @@ import ( "flag" "fmt" "go/ast" + "go/format" "go/parser" - "go/printer" "go/scanner" "go/token" "io/ioutil" @@ -97,23 +97,11 @@ func main() { os.Exit(exitCode) } -const ( - tabWidth = 8 - parserMode = parser.ParseComments - printerMode = printer.TabIndent | printer.UseSpaces -) - -var printConfig = &printer.Config{ - Mode: printerMode, - Tabwidth: tabWidth, -} +const parserMode = parser.ParseComments func gofmtFile(f *ast.File) ([]byte, error) { var buf bytes.Buffer - - ast.SortImports(fset, f) - err := printConfig.Fprint(&buf, fset, f) - if err != nil { + if err := format.Node(&buf, fset, f); err != nil { return nil, err } return buf.Bytes(), nil @@ -211,8 +199,7 @@ var gofmtBuf bytes.Buffer func gofmt(n interface{}) string { gofmtBuf.Reset() - err := printConfig.Fprint(&gofmtBuf, fset, n) - if err != nil { + if err := format.Node(&gofmtBuf, fset, n); err != nil { return "<" + err.Error() + ">" } return gofmtBuf.String() diff --git a/src/cmd/fix/mapdelete.go b/src/cmd/fix/mapdelete.go deleted file mode 100644 index db89c7bf4..000000000 --- a/src/cmd/fix/mapdelete.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(mapdeleteFix) -} - -var mapdeleteFix = fix{ - "mapdelete", - "2011-10-18", - mapdelete, - `Use delete(m, k) instead of m[k] = 0, false. - -http://codereview.appspot.com/5272045 -`, -} - -func mapdelete(f *ast.File) bool { - fixed := false - walk(f, func(n interface{}) { - stmt, ok := n.(*ast.Stmt) - if !ok { - return - } - as, ok := (*stmt).(*ast.AssignStmt) - if !ok || len(as.Lhs) != 1 || len(as.Rhs) != 2 { - return - } - ix, ok := as.Lhs[0].(*ast.IndexExpr) - if !ok { - return - } - if !isTopName(as.Rhs[1], "false") { - warn(as.Pos(), "two-element map assignment with non-false second value") - return - } - if !canDrop(as.Rhs[0]) { - warn(as.Pos(), "two-element map assignment with non-trivial first value") - return - } - *stmt = &ast.ExprStmt{ - X: &ast.CallExpr{ - Fun: &ast.Ident{ - NamePos: as.Pos(), - Name: "delete", - }, - Args: []ast.Expr{ix.X, ix.Index}, - }, - } - fixed = true - }) - return fixed -} - -// canDrop reports whether it is safe to drop the -// evaluation of n from the program. -// It is very conservative. -func canDrop(n ast.Expr) bool { - switch n := n.(type) { - case *ast.Ident, *ast.BasicLit: - return true - case *ast.ParenExpr: - return canDrop(n.X) - case *ast.SelectorExpr: - return canDrop(n.X) - case *ast.CompositeLit: - if !canDrop(n.Type) { - return false - } - for _, e := range n.Elts { - if !canDrop(e) { - return false - } - } - return true - case *ast.StarExpr: - // Dropping *x is questionable, - // but we have to be able to drop (*T)(nil). - return canDrop(n.X) - case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType: - return true - } - return false -} diff --git a/src/cmd/fix/mapdelete_test.go b/src/cmd/fix/mapdelete_test.go deleted file mode 100644 index 8ed50328e..000000000 --- a/src/cmd/fix/mapdelete_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(mapdeleteTests, mapdelete) -} - -var mapdeleteTests = []testCase{ - { - Name: "mapdelete.0", - In: `package main - -func f() { - m[x] = 0, false - m[x] = g(), false - m[x] = 1 - delete(m, x) - m[x] = 0, b -} - -func g(false bool) { - m[x] = 0, false -} -`, - Out: `package main - -func f() { - delete(m, x) - m[x] = g(), false - m[x] = 1 - delete(m, x) - m[x] = 0, b -} - -func g(false bool) { - m[x] = 0, false -} -`, - }, -} diff --git a/src/cmd/fix/math.go b/src/cmd/fix/math.go deleted file mode 100644 index 2ec837eb0..000000000 --- a/src/cmd/fix/math.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(mathFix) -} - -var mathFix = fix{ - "math", - "2011-09-29", - math, - `Remove the leading F from math functions such as Fabs. - -http://codereview.appspot.com/5158043 -`, -} - -var mathRenames = []struct{ in, out string }{ - {"Fabs", "Abs"}, - {"Fdim", "Dim"}, - {"Fmax", "Max"}, - {"Fmin", "Min"}, - {"Fmod", "Mod"}, -} - -func math(f *ast.File) bool { - if !imports(f, "math") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions. - if expr, ok := n.(ast.Expr); ok { - for _, s := range mathRenames { - if isPkgDot(expr, "math", s.in) { - expr.(*ast.SelectorExpr).Sel.Name = s.out - fixed = true - return - } - } - } - }) - return fixed -} diff --git a/src/cmd/fix/math_test.go b/src/cmd/fix/math_test.go deleted file mode 100644 index b8d69d2f2..000000000 --- a/src/cmd/fix/math_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(mathTests, math) -} - -var mathTests = []testCase{ - { - Name: "math.0", - In: `package main - -import ( - "math" -) - -func f() { - math.Fabs(1) - math.Fdim(1) - math.Fmax(1) - math.Fmin(1) - math.Fmod(1) - math.Abs(1) - foo.Fabs(1) -} -`, - Out: `package main - -import ( - "math" -) - -func f() { - math.Abs(1) - math.Dim(1) - math.Max(1) - math.Min(1) - math.Mod(1) - math.Abs(1) - foo.Fabs(1) -} -`, - }, -} diff --git a/src/cmd/fix/netdial.go b/src/cmd/fix/netdial.go deleted file mode 100644 index 2de994cff..000000000 --- a/src/cmd/fix/netdial.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(netdialFix) - register(tlsdialFix) - register(netlookupFix) -} - -var netdialFix = fix{ - "netdial", - "2011-03-28", - netdial, - `Adapt 3-argument calls of net.Dial to use 2-argument form. - -http://codereview.appspot.com/4244055 -`, -} - -var tlsdialFix = fix{ - "tlsdial", - "2011-03-28", - tlsdial, - `Adapt 4-argument calls of tls.Dial to use 3-argument form. - -http://codereview.appspot.com/4244055 -`, -} - -var netlookupFix = fix{ - "netlookup", - "2011-03-28", - netlookup, - `Adapt 3-result calls to net.LookupHost to use 2-result form. - -http://codereview.appspot.com/4244055 -`, -} - -func netdial(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "net", "Dial") || len(call.Args) != 3 { - return - } - // net.Dial(a, "", b) -> net.Dial(a, b) - if !isEmptyString(call.Args[1]) { - warn(call.Pos(), "call to net.Dial with non-empty second argument") - return - } - call.Args[1] = call.Args[2] - call.Args = call.Args[:2] - fixed = true - }) - return fixed -} - -func tlsdial(f *ast.File) bool { - if !imports(f, "crypto/tls") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "tls", "Dial") || len(call.Args) != 4 { - return - } - // tls.Dial(a, "", b, c) -> tls.Dial(a, b, c) - if !isEmptyString(call.Args[1]) { - warn(call.Pos(), "call to tls.Dial with non-empty second argument") - return - } - call.Args[1] = call.Args[2] - call.Args[2] = call.Args[3] - call.Args = call.Args[:3] - fixed = true - }) - return fixed -} - -func netlookup(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 3 || len(as.Rhs) != 1 { - return - } - call, ok := as.Rhs[0].(*ast.CallExpr) - if !ok || !isPkgDot(call.Fun, "net", "LookupHost") { - return - } - if !isBlank(as.Lhs[2]) { - warn(as.Pos(), "call to net.LookupHost expecting cname; use net.LookupCNAME") - return - } - as.Lhs = as.Lhs[:2] - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/netdial_test.go b/src/cmd/fix/netdial_test.go deleted file mode 100644 index fff00b4ad..000000000 --- a/src/cmd/fix/netdial_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(netdialTests, nil) -} - -var netdialTests = []testCase{ - { - Name: "netdial.0", - Fn: netdial, - In: `package main - -import "net" - -func f() { - c, err := net.Dial(net, "", addr) - c, err = net.Dial(net, "", addr) -} -`, - Out: `package main - -import "net" - -func f() { - c, err := net.Dial(net, addr) - c, err = net.Dial(net, addr) -} -`, - }, - - { - Name: "netlookup.0", - Fn: netlookup, - In: `package main - -import "net" - -func f() { - foo, bar, _ := net.LookupHost(host) - foo, bar, _ = net.LookupHost(host) -} -`, - Out: `package main - -import "net" - -func f() { - foo, bar := net.LookupHost(host) - foo, bar = net.LookupHost(host) -} -`, - }, -} diff --git a/src/cmd/fix/netipv6zone.go b/src/cmd/fix/netipv6zone.go new file mode 100644 index 000000000..587b9ffec --- /dev/null +++ b/src/cmd/fix/netipv6zone.go @@ -0,0 +1,71 @@ +// 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 main + +import "go/ast" + +func init() { + register(netipv6zoneFix) +} + +var netipv6zoneFix = fix{ + "netipv6zone", + "2012-11-26", + netipv6zone, + `Adapt element key to IPNet, IPAddr, UDPAddr or TCPAddr composite literals. + +https://codereview.appspot.com/6849045/ +`, +} + +func netipv6zone(f *ast.File) bool { + if !imports(f, "net") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + cl, ok := n.(*ast.CompositeLit) + if !ok { + return + } + se, ok := cl.Type.(*ast.SelectorExpr) + if !ok { + return + } + if !isTopName(se.X, "net") || se.Sel == nil { + return + } + switch ss := se.Sel.String(); ss { + case "IPNet", "IPAddr", "UDPAddr", "TCPAddr": + for i, e := range cl.Elts { + if _, ok := e.(*ast.KeyValueExpr); ok { + break + } + switch i { + case 0: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("IP"), + Value: e, + } + case 1: + if ss == "IPNet" { + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Mask"), + Value: e, + } + } else { + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Port"), + Value: e, + } + } + } + fixed = true + } + } + }) + return fixed +} diff --git a/src/cmd/fix/netipv6zone_test.go b/src/cmd/fix/netipv6zone_test.go new file mode 100644 index 000000000..229daa386 --- /dev/null +++ b/src/cmd/fix/netipv6zone_test.go @@ -0,0 +1,51 @@ +// 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 main + +func init() { + addTestCases(netipv6zoneTests, netipv6zone) +} + +var netipv6zoneTests = []testCase{ + { + Name: "netipv6zone.0", + In: `package main + +import "net" + +var a = []struct { + *net.IPNet +}{ + &net.IPNet{net.ParseIP("2001:DB8::"), net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, +} + +func f() net.Addr { + b := net.IPNet{net.IPv4(127, 0, 0, 1), net.IPv4Mask(255, 0, 0, 0)} + c := &net.IPAddr{ip1} + sub(&net.UDPAddr{ip2, 12345}) + d := &net.TCPAddr{IP: ip3, Port: 54321} + return &net.TCPAddr{ip4}, nil +} +`, + Out: `package main + +import "net" + +var a = []struct { + *net.IPNet +}{ + &net.IPNet{IP: net.ParseIP("2001:DB8::"), Mask: net.IPMask(net.ParseIP("ffff:ffff:ffff::"))}, +} + +func f() net.Addr { + b := net.IPNet{IP: net.IPv4(127, 0, 0, 1), Mask: net.IPv4Mask(255, 0, 0, 0)} + c := &net.IPAddr{IP: ip1} + sub(&net.UDPAddr{IP: ip2, Port: 12345}) + d := &net.TCPAddr{IP: ip3, Port: 54321} + return &net.TCPAddr{IP: ip4}, nil +} +`, + }, +} diff --git a/src/cmd/fix/netudpgroup.go b/src/cmd/fix/netudpgroup.go deleted file mode 100644 index b54beb0de..000000000 --- a/src/cmd/fix/netudpgroup.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(netudpgroupFix) -} - -var netudpgroupFix = fix{ - "netudpgroup", - "2011-08-18", - netudpgroup, - `Adapt 1-argument calls of net.(*UDPConn).JoinGroup, LeaveGroup to use 2-argument form. - -http://codereview.appspot.com/4815074 -`, -} - -func netudpgroup(f *ast.File) bool { - if !imports(f, "net") { - return false - } - - fixed := false - for _, d := range f.Decls { - fd, ok := d.(*ast.FuncDecl) - if !ok || fd.Body == nil { - continue - } - walk(fd.Body, func(n interface{}) { - ce, ok := n.(*ast.CallExpr) - if !ok { - return - } - se, ok := ce.Fun.(*ast.SelectorExpr) - if !ok || len(ce.Args) != 1 { - return - } - switch se.Sel.String() { - case "JoinGroup", "LeaveGroup": - // c.JoinGroup(a) -> c.JoinGroup(nil, a) - // c.LeaveGroup(a) -> c.LeaveGroup(nil, a) - arg := ce.Args[0] - ce.Args = make([]ast.Expr, 2) - ce.Args[0] = ast.NewIdent("nil") - ce.Args[1] = arg - fixed = true - } - }) - } - return fixed -} diff --git a/src/cmd/fix/netudpgroup_test.go b/src/cmd/fix/netudpgroup_test.go deleted file mode 100644 index 88c0e093f..000000000 --- a/src/cmd/fix/netudpgroup_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(netudpgroupTests, netudpgroup) -} - -var netudpgroupTests = []testCase{ - { - Name: "netudpgroup.0", - In: `package main - -import "net" - -func f() { - err := x.JoinGroup(gaddr) - err = y.LeaveGroup(gaddr) -} -`, - Out: `package main - -import "net" - -func f() { - err := x.JoinGroup(nil, gaddr) - err = y.LeaveGroup(nil, gaddr) -} -`, - }, - // Innocent function with no body. - { - Name: "netudpgroup.1", - In: `package main - -import "net" - -func f() - -var _ net.IP -`, - Out: `package main - -import "net" - -func f() - -var _ net.IP -`, - }, -} diff --git a/src/cmd/fix/newwriter.go b/src/cmd/fix/newwriter.go deleted file mode 100644 index 4befe24fb..000000000 --- a/src/cmd/fix/newwriter.go +++ /dev/null @@ -1,90 +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 main - -import ( - "go/ast" -) - -func init() { - register(newWriterFix) -} - -var newWriterFix = fix{ - "newWriter", - "2012-02-14", - newWriter, - `Adapt bufio, gzip and zlib NewWriterXxx calls for whether they return errors. - -Also rename gzip.Compressor and gzip.Decompressor to gzip.Writer and gzip.Reader. - -http://codereview.appspot.com/5639057 and -http://codereview.appspot.com/5642054 -`, -} - -func newWriter(f *ast.File) bool { - if !imports(f, "bufio") && !imports(f, "compress/gzip") && !imports(f, "compress/zlib") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - switch n := n.(type) { - case *ast.SelectorExpr: - if isTopName(n.X, "gzip") { - switch n.Sel.String() { - case "Compressor": - n.Sel = &ast.Ident{Name: "Writer"} - fixed = true - case "Decompressor": - n.Sel = &ast.Ident{Name: "Reader"} - fixed = true - } - } else if isTopName(n.X, "zlib") { - if n.Sel.String() == "NewWriterDict" { - n.Sel = &ast.Ident{Name: "NewWriterLevelDict"} - fixed = true - } - } - - case *ast.AssignStmt: - // Drop the ", _" in assignments of the form: - // w0, _ = gzip.NewWriter(w1) - if len(n.Lhs) != 2 || len(n.Rhs) != 1 { - return - } - i, ok := n.Lhs[1].(*ast.Ident) - if !ok { - return - } - if i.String() != "_" { - return - } - c, ok := n.Rhs[0].(*ast.CallExpr) - if !ok { - return - } - s, ok := c.Fun.(*ast.SelectorExpr) - if !ok { - return - } - sel := s.Sel.String() - switch { - case isTopName(s.X, "bufio") && (sel == "NewReaderSize" || sel == "NewWriterSize"): - // No-op. - case isTopName(s.X, "gzip") && sel == "NewWriter": - // No-op. - case isTopName(s.X, "zlib") && sel == "NewWriter": - // No-op. - default: - return - } - n.Lhs = n.Lhs[:1] - fixed = true - } - }) - return fixed -} diff --git a/src/cmd/fix/newwriter_test.go b/src/cmd/fix/newwriter_test.go deleted file mode 100644 index 1f59628a0..000000000 --- a/src/cmd/fix/newwriter_test.go +++ /dev/null @@ -1,83 +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 main - -func init() { - addTestCases(newWriterTests, newWriter) -} - -var newWriterTests = []testCase{ - { - Name: "newWriter.0", - In: `package main - -import ( - "bufio" - "compress/gzip" - "compress/zlib" - "io" - - "foo" -) - -func f() *gzip.Compressor { - var ( - _ gzip.Compressor - _ *gzip.Decompressor - _ struct { - W *gzip.Compressor - R gzip.Decompressor - } - ) - - var w io.Writer - br := bufio.NewReader(nil) - br, _ = bufio.NewReaderSize(nil, 256) - bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable. - bw, _ = bufio.NewWriterSize(w, 256) - fw, _ := foo.NewWriter(w) - gw, _ := gzip.NewWriter(w) - gw, _ = gzip.NewWriter(w) - zw, _ := zlib.NewWriter(w) - _ = zlib.NewWriterDict(zw, 0, nil) - return gw -} -`, - Out: `package main - -import ( - "bufio" - "compress/gzip" - "compress/zlib" - "io" - - "foo" -) - -func f() *gzip.Writer { - var ( - _ gzip.Writer - _ *gzip.Reader - _ struct { - W *gzip.Writer - R gzip.Reader - } - ) - - var w io.Writer - br := bufio.NewReader(nil) - br = bufio.NewReaderSize(nil, 256) - bw, err := bufio.NewWriterSize(w, 256) // Unfixable, as it declares an err variable. - bw = bufio.NewWriterSize(w, 256) - fw, _ := foo.NewWriter(w) - gw := gzip.NewWriter(w) - gw = gzip.NewWriter(w) - zw := zlib.NewWriter(w) - _ = zlib.NewWriterLevelDict(zw, 0, nil) - return gw -} -`, - }, -} diff --git a/src/cmd/fix/oserrorstring.go b/src/cmd/fix/oserrorstring.go deleted file mode 100644 index a75a2c12d..000000000 --- a/src/cmd/fix/oserrorstring.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(oserrorstringFix) -} - -var oserrorstringFix = fix{ - "oserrorstring", - "2011-06-22", - oserrorstring, - `Replace os.ErrorString() conversions with calls to os.NewError(). - -http://codereview.appspot.com/4607052 -`, -} - -func oserrorstring(f *ast.File) bool { - if !imports(f, "os") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // The conversion os.ErrorString(x) looks like a call - // of os.ErrorString with one argument. - if call := callExpr(n, "os", "ErrorString"); call != nil { - // os.ErrorString(args) -> os.NewError(args) - call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" - // os.ErrorString(args) -> os.NewError(args) - call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" - fixed = true - return - } - - // Remove os.Error type from variable declarations initialized - // with an os.NewError. - // (An *ast.ValueSpec may also be used in a const declaration - // but those won't be initialized with a call to os.NewError.) - if spec, ok := n.(*ast.ValueSpec); ok && - len(spec.Names) == 1 && - isPkgDot(spec.Type, "os", "Error") && - len(spec.Values) == 1 && - callExpr(spec.Values[0], "os", "NewError") != nil { - // var name os.Error = os.NewError(x) -> - // var name = os.NewError(x) - spec.Type = nil - fixed = true - return - } - - // Other occurrences of os.ErrorString are not fixed - // but they are rare. - - }) - return fixed -} - -// callExpr returns the call expression if x is a call to pkg.name with one argument; -// otherwise it returns nil. -func callExpr(x interface{}, pkg, name string) *ast.CallExpr { - if call, ok := x.(*ast.CallExpr); ok && - len(call.Args) == 1 && - isPkgDot(call.Fun, pkg, name) { - return call - } - return nil -} diff --git a/src/cmd/fix/oserrorstring_test.go b/src/cmd/fix/oserrorstring_test.go deleted file mode 100644 index 75551480c..000000000 --- a/src/cmd/fix/oserrorstring_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(oserrorstringTests, oserrorstring) -} - -var oserrorstringTests = []testCase{ - { - Name: "oserrorstring.0", - In: `package main - -import "os" - -var _ = os.ErrorString("foo") -var _ os.Error = os.ErrorString("bar1") -var _ os.Error = os.NewError("bar2") -var _ os.Error = MyError("bal") // don't rewrite this one - -var ( - _ = os.ErrorString("foo") - _ os.Error = os.ErrorString("bar1") - _ os.Error = os.NewError("bar2") - _ os.Error = MyError("bal") // don't rewrite this one -) - -func _() (err os.Error) { - err = os.ErrorString("foo") - return os.ErrorString("foo") -} -`, - Out: `package main - -import "os" - -var _ = os.NewError("foo") -var _ = os.NewError("bar1") -var _ = os.NewError("bar2") -var _ os.Error = MyError("bal") // don't rewrite this one - -var ( - _ = os.NewError("foo") - _ = os.NewError("bar1") - _ = os.NewError("bar2") - _ os.Error = MyError("bal") // don't rewrite this one -) - -func _() (err os.Error) { - err = os.NewError("foo") - return os.NewError("foo") -} -`, - }, -} diff --git a/src/cmd/fix/osopen.go b/src/cmd/fix/osopen.go deleted file mode 100644 index af2796ac2..000000000 --- a/src/cmd/fix/osopen.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(osopenFix) -} - -var osopenFix = fix{ - "osopen", - "2011-04-04", - osopen, - `Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE. - -http://codereview.appspot.com/4357052 -`, -} - -func osopen(f *ast.File) bool { - if !imports(f, "os") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - // Rename O_CREAT to O_CREATE. - if expr, ok := n.(ast.Expr); ok && isPkgDot(expr, "os", "O_CREAT") { - expr.(*ast.SelectorExpr).Sel.Name = "O_CREATE" - fixed = true - return - } - - // Fix up calls to Open. - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 3 { - return - } - if !isPkgDot(call.Fun, "os", "Open") { - return - } - sel := call.Fun.(*ast.SelectorExpr) - args := call.Args - // os.Open(a, os.O_RDONLY, c) -> os.Open(a) - if isPkgDot(args[1], "os", "O_RDONLY") || isPkgDot(args[1], "syscall", "O_RDONLY") { - call.Args = call.Args[0:1] - fixed = true - return - } - // os.Open(a, createlike_flags, c) -> os.Create(a, c) - if isCreateFlag(args[1]) { - sel.Sel.Name = "Create" - if !isSimplePerm(args[2]) { - warn(sel.Pos(), "rewrote os.Open to os.Create with permission not 0666") - } - call.Args = args[0:1] - fixed = true - return - } - // Fallback: os.Open(a, b, c) -> os.OpenFile(a, b, c) - sel.Sel.Name = "OpenFile" - fixed = true - }) - return fixed -} - -func isCreateFlag(flag ast.Expr) bool { - foundCreate := false - foundTrunc := false - // OR'ing of flags: is O_CREATE on? + or | would be fine; we just look for os.O_CREATE - // and don't worry about the actual operator. - p := flag.Pos() - for { - lhs := flag - expr, isBinary := flag.(*ast.BinaryExpr) - if isBinary { - lhs = expr.Y - } - sel, ok := lhs.(*ast.SelectorExpr) - if !ok || !isTopName(sel.X, "os") { - return false - } - switch sel.Sel.Name { - case "O_CREATE": - foundCreate = true - case "O_TRUNC": - foundTrunc = true - case "O_RDONLY", "O_WRONLY", "O_RDWR": - // okay - default: - // Unexpected flag, like O_APPEND or O_EXCL. - // Be conservative and do not rewrite. - return false - } - if !isBinary { - break - } - flag = expr.X - } - if !foundCreate { - return false - } - if !foundTrunc { - warn(p, "rewrote os.Open with O_CREATE but not O_TRUNC to os.Create") - } - return foundCreate -} - -func isSimplePerm(perm ast.Expr) bool { - basicLit, ok := perm.(*ast.BasicLit) - if !ok { - return false - } - switch basicLit.Value { - case "0666": - return true - } - return false -} diff --git a/src/cmd/fix/osopen_test.go b/src/cmd/fix/osopen_test.go deleted file mode 100644 index 5797adb7b..000000000 --- a/src/cmd/fix/osopen_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(osopenTests, osopen) -} - -var osopenTests = []testCase{ - { - Name: "osopen.0", - In: `package main - -import ( - "os" -) - -func f() { - os.OpenFile(a, b, c) - os.Open(a, os.O_RDONLY, 0) - os.Open(a, os.O_RDONLY, 0666) - os.Open(a, os.O_RDWR, 0) - os.Open(a, os.O_CREAT, 0666) - os.Open(a, os.O_CREAT|os.O_TRUNC, 0664) - os.Open(a, os.O_CREATE, 0666) - os.Open(a, os.O_CREATE|os.O_TRUNC, 0664) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - os.Open(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) - os.Open(a, os.O_SURPRISE|os.O_CREATE, 0666) - _ = os.O_CREAT -} -`, - Out: `package main - -import ( - "os" -) - -func f() { - os.OpenFile(a, b, c) - os.Open(a) - os.Open(a) - os.OpenFile(a, os.O_RDWR, 0) - os.Create(a) - os.Create(a) - os.Create(a) - os.Create(a) - os.Create(a) - os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) - os.OpenFile(a, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666) - os.OpenFile(a, os.O_SURPRISE|os.O_CREATE, 0666) - _ = os.O_CREATE -} -`, - }, - { - Name: "osopen.1", - In: `package main - -import ( - "os" -) - -func f() { - _ = os.O_CREAT -} -`, - Out: `package main - -import ( - "os" -) - -func f() { - _ = os.O_CREATE -} -`, - }, -} diff --git a/src/cmd/fix/printerconfig.go b/src/cmd/fix/printerconfig.go new file mode 100644 index 000000000..432e18bb2 --- /dev/null +++ b/src/cmd/fix/printerconfig.go @@ -0,0 +1,61 @@ +// 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 main + +import "go/ast" + +func init() { + register(printerconfigFix) +} + +var printerconfigFix = fix{ + "printerconfig", + "2012-12-11", + printerconfig, + `Add element keys to Config composite literals.`, +} + +func printerconfig(f *ast.File) bool { + if !imports(f, "go/printer") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + cl, ok := n.(*ast.CompositeLit) + if !ok { + return + } + se, ok := cl.Type.(*ast.SelectorExpr) + if !ok { + return + } + if !isTopName(se.X, "printer") || se.Sel == nil { + return + } + + if ss := se.Sel.String(); ss == "Config" { + for i, e := range cl.Elts { + if _, ok := e.(*ast.KeyValueExpr); ok { + break + } + switch i { + case 0: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Mode"), + Value: e, + } + case 1: + cl.Elts[i] = &ast.KeyValueExpr{ + Key: ast.NewIdent("Tabwidth"), + Value: e, + } + } + fixed = true + } + } + }) + return fixed +} diff --git a/src/cmd/fix/printerconfig_test.go b/src/cmd/fix/printerconfig_test.go new file mode 100644 index 000000000..72e2bdcac --- /dev/null +++ b/src/cmd/fix/printerconfig_test.go @@ -0,0 +1,37 @@ +// 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 main + +func init() { + addTestCases(printerconfigTests, printerconfig) +} + +var printerconfigTests = []testCase{ + { + Name: "printerconfig.0", + In: `package main + +import "go/printer" + +func f() printer.Config { + b := printer.Config{0, 8} + c := &printer.Config{0} + d := &printer.Config{Tabwidth: 8, Mode: 0} + return printer.Config{0, 8} +} +`, + Out: `package main + +import "go/printer" + +func f() printer.Config { + b := printer.Config{Mode: 0, Tabwidth: 8} + c := &printer.Config{Mode: 0} + d := &printer.Config{Tabwidth: 8, Mode: 0} + return printer.Config{Mode: 0, Tabwidth: 8} +} +`, + }, +} diff --git a/src/cmd/fix/procattr.go b/src/cmd/fix/procattr.go deleted file mode 100644 index ea375ec9d..000000000 --- a/src/cmd/fix/procattr.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(procattrFix) -} - -var procattrFix = fix{ - "procattr", - "2011-03-15", - procattr, - `Adapt calls to os.StartProcess to use new ProcAttr type. - -http://codereview.appspot.com/4253052 -`, -} - -func procattr(f *ast.File) bool { - if !imports(f, "os") && !imports(f, "syscall") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 5 { - return - } - var pkg string - if isPkgDot(call.Fun, "os", "StartProcess") { - pkg = "os" - } else if isPkgDot(call.Fun, "syscall", "StartProcess") { - pkg = "syscall" - } else { - return - } - // os.StartProcess(a, b, c, d, e) -> os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) - lit := &ast.CompositeLit{Type: ast.NewIdent(pkg + ".ProcAttr")} - env, dir, files := call.Args[2], call.Args[3], call.Args[4] - if !isName(env, "nil") && !isCall(env, "os", "Environ") { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Env"), Value: env}) - } - if !isEmptyString(dir) { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Dir"), Value: dir}) - } - if !isName(files, "nil") { - lit.Elts = append(lit.Elts, &ast.KeyValueExpr{Key: ast.NewIdent("Files"), Value: files}) - } - call.Args[2] = &ast.UnaryExpr{Op: token.AND, X: lit} - call.Args = call.Args[:3] - fixed = true - }) - return fixed -} diff --git a/src/cmd/fix/procattr_test.go b/src/cmd/fix/procattr_test.go deleted file mode 100644 index 9e2b86e74..000000000 --- a/src/cmd/fix/procattr_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(procattrTests, procattr) -} - -var procattrTests = []testCase{ - { - Name: "procattr.0", - In: `package main - -import ( - "os" - "syscall" -) - -func f() { - os.StartProcess(a, b, c, d, e) - os.StartProcess(a, b, os.Environ(), d, e) - os.StartProcess(a, b, nil, d, e) - os.StartProcess(a, b, c, "", e) - os.StartProcess(a, b, c, d, nil) - os.StartProcess(a, b, nil, "", nil) - - os.StartProcess( - a, - b, - c, - d, - e, - ) - - syscall.StartProcess(a, b, c, d, e) - syscall.StartProcess(a, b, os.Environ(), d, e) - syscall.StartProcess(a, b, nil, d, e) - syscall.StartProcess(a, b, c, "", e) - syscall.StartProcess(a, b, c, d, nil) - syscall.StartProcess(a, b, nil, "", nil) -} -`, - Out: `package main - -import ( - "os" - "syscall" -) - -func f() { - os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Dir: d, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Env: c, Files: e}) - os.StartProcess(a, b, &os.ProcAttr{Env: c, Dir: d}) - os.StartProcess(a, b, &os.ProcAttr{}) - - os.StartProcess( - a, - b, &os.ProcAttr{Env: c, Dir: d, Files: e}, - ) - - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Dir: d, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Files: e}) - syscall.StartProcess(a, b, &syscall.ProcAttr{Env: c, Dir: d}) - syscall.StartProcess(a, b, &syscall.ProcAttr{}) -} -`, - }, -} diff --git a/src/cmd/fix/reflect.go b/src/cmd/fix/reflect.go deleted file mode 100644 index 151da569d..000000000 --- a/src/cmd/fix/reflect.go +++ /dev/null @@ -1,862 +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. - -// TODO(rsc): Once there is better support for writing -// multi-package commands, this should really be in -// its own package, and then we can drop all the "reflect" -// prefixes on the global variables and functions. - -package main - -import ( - "go/ast" - "go/token" - "strings" -) - -func init() { - register(reflectFix) -} - -var reflectFix = fix{ - "reflect", - "2011-04-08", - reflectFn, - `Adapt code to new reflect API. - -http://codereview.appspot.com/4281055 -http://codereview.appspot.com/4433066 -`, -} - -// The reflect API change dropped the concrete types *reflect.ArrayType etc. -// Any type assertions prior to method calls can be deleted: -// x.(*reflect.ArrayType).Len() -> x.Len() -// -// Any type checks can be replaced by assignment and check of Kind: -// x, y := z.(*reflect.ArrayType) -// -> -// x := z -// y := x.Kind() == reflect.Array -// -// If z is an ordinary variable name and x is not subsequently assigned to, -// references to x can be replaced by z and the assignment deleted. -// We only bother if x and z are the same name. -// If y is not subsequently assigned to and neither is x, references to -// y can be replaced by its expression. We only bother when there is -// just one use or when the use appears in an if clause. -// -// Not all type checks result in a single Kind check. The rewrite of the type check for -// reflect.ArrayOrSliceType checks x.Kind() against reflect.Array and reflect.Slice. -// The rewrite for *reflect.IntType checks against Int, Int8, Int16, Int32, Int64. -// The rewrite for *reflect.UintType adds Uintptr. -// -// A type switch turns into an assignment and a switch on Kind: -// switch x := y.(type) { -// case reflect.ArrayOrSliceType: -// ... -// case *reflect.ChanType: -// ... -// case *reflect.IntType: -// ... -// } -// -> -// switch x := y; x.Kind() { -// case reflect.Array, reflect.Slice: -// ... -// case reflect.Chan: -// ... -// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: -// ... -// } -// -// The same simplification applies: we drop x := x if x is not assigned -// to in the switch cases. -// -// Because the type check assignment includes a type assertion in its -// syntax and the rewrite traversal is bottom up, we must do a pass to -// rewrite the type check assignments and then a separate pass to -// rewrite the type assertions. -// -// The same process applies to the API changes for reflect.Value. -// -// For both cases, but especially Value, the code needs to be aware -// of the type of a receiver when rewriting a method call. For example, -// x.(*reflect.ArrayValue).Elem(i) becomes x.Index(i) while -// x.(*reflect.MapValue).Elem(v) becomes x.MapIndex(v). -// In general, reflectFn needs to know the type of the receiver expression. -// In most cases (and in all the cases in the Go source tree), the toy -// type checker in typecheck.go provides enough information for fix -// to make the rewrite. If fix misses a rewrite, the code that is left over -// will not compile, so it will be noticed immediately. - -func reflectFn(f *ast.File) bool { - if !imports(f, "reflect") { - return false - } - - fixed := false - - // Rewrite names in method calls. - // Needs basic type information (see above). - typeof, _ := typecheck(reflectTypeConfig, f) - walk(f, func(n interface{}) { - switch n := n.(type) { - case *ast.SelectorExpr: - typ := typeof[n.X] - if m := reflectRewriteMethod[typ]; m != nil { - if replace := m[n.Sel.Name]; replace != "" { - n.Sel.Name = replace - fixed = true - return - } - } - - // For all reflect Values, replace SetValue with Set. - if isReflectValue[typ] && n.Sel.Name == "SetValue" { - n.Sel.Name = "Set" - fixed = true - return - } - - // Replace reflect.MakeZero with reflect.Zero. - if isPkgDot(n, "reflect", "MakeZero") { - n.Sel.Name = "Zero" - fixed = true - return - } - } - }) - - // Replace PtrValue's PointTo(x) with Set(x.Addr()). - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 1 { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || sel.Sel.Name != "PointTo" { - return - } - typ := typeof[sel.X] - if typ != "*reflect.PtrValue" { - return - } - sel.Sel.Name = "Set" - if !isTopName(call.Args[0], "nil") { - call.Args[0] = &ast.SelectorExpr{ - X: call.Args[0], - Sel: ast.NewIdent("Addr()"), - } - } - fixed = true - }) - - // Fix type switches. - walk(f, func(n interface{}) { - if reflectFixSwitch(n) { - fixed = true - } - }) - - // Fix type assertion checks (multiple assignment statements). - // Have to work on the statement context (statement list or if statement) - // so that we can insert an extra statement occasionally. - // Ignoring for and switch because they don't come up in - // typical code. - walk(f, func(n interface{}) { - switch n := n.(type) { - case *[]ast.Stmt: - // v is the replacement statement list. - var v []ast.Stmt - insert := func(x ast.Stmt) { - v = append(v, x) - } - for i, x := range *n { - // Tentatively append to v; if we rewrite x - // we'll have to update the entry, so remember - // the index. - j := len(v) - v = append(v, x) - if reflectFixTypecheck(&x, insert, (*n)[i+1:]) { - // reflectFixTypecheck may have overwritten x. - // Update the entry we appended just before the call. - v[j] = x - fixed = true - } - } - *n = v - case *ast.IfStmt: - x := &ast.ExprStmt{X: n.Cond} - if reflectFixTypecheck(&n.Init, nil, []ast.Stmt{x, n.Body, n.Else}) { - n.Cond = x.X - fixed = true - } - } - }) - - // Warn about any typecheck statements that we missed. - walk(f, reflectWarnTypecheckStmt) - - // Now that those are gone, fix remaining type assertions. - // Delayed because the type checks have - // type assertions as part of their syntax. - walk(f, func(n interface{}) { - if reflectFixAssert(n) { - fixed = true - } - }) - - // Now that the type assertions are gone, rewrite remaining - // references to specific reflect types to use the general ones. - walk(f, func(n interface{}) { - ptr, ok := n.(*ast.Expr) - if !ok { - return - } - nn := *ptr - typ := reflectType(nn) - if typ == "" { - return - } - if strings.HasSuffix(typ, "Type") { - *ptr = newPkgDot(nn.Pos(), "reflect", "Type") - } else { - *ptr = newPkgDot(nn.Pos(), "reflect", "Value") - } - fixed = true - }) - - // Rewrite v.Set(nil) to v.Set(reflect.MakeZero(v.Type())). - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) != 1 || !isTopName(call.Args[0], "nil") { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || !isReflectValue[typeof[sel.X]] || sel.Sel.Name != "Set" { - return - } - call.Args[0] = &ast.CallExpr{ - Fun: newPkgDot(call.Args[0].Pos(), "reflect", "Zero"), - Args: []ast.Expr{ - &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: sel.X, - Sel: &ast.Ident{Name: "Type"}, - }, - }, - }, - } - fixed = true - }) - - // Rewrite v != nil to v.IsValid(). - // Rewrite nil used as reflect.Value (in function argument or return) to reflect.Value{}. - walk(f, func(n interface{}) { - ptr, ok := n.(*ast.Expr) - if !ok { - return - } - if isTopName(*ptr, "nil") && isReflectValue[typeof[*ptr]] { - *ptr = ast.NewIdent("reflect.Value{}") - fixed = true - return - } - nn, ok := (*ptr).(*ast.BinaryExpr) - if !ok || (nn.Op != token.EQL && nn.Op != token.NEQ) || !isTopName(nn.Y, "nil") || !isReflectValue[typeof[nn.X]] { - return - } - var call ast.Expr = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: nn.X, - Sel: &ast.Ident{Name: "IsValid"}, - }, - } - if nn.Op == token.EQL { - call = &ast.UnaryExpr{Op: token.NOT, X: call} - } - *ptr = call - fixed = true - }) - - // Rewrite - // reflect.Typeof -> reflect.TypeOf, - walk(f, func(n interface{}) { - sel, ok := n.(*ast.SelectorExpr) - if !ok { - return - } - if isTopName(sel.X, "reflect") && sel.Sel.Name == "Typeof" { - sel.Sel.Name = "TypeOf" - fixed = true - } - if isTopName(sel.X, "reflect") && sel.Sel.Name == "NewValue" { - sel.Sel.Name = "ValueOf" - fixed = true - } - }) - - return fixed -} - -// reflectFixSwitch rewrites *n (if n is an *ast.Stmt) corresponding -// to a type switch. -func reflectFixSwitch(n interface{}) bool { - ptr, ok := n.(*ast.Stmt) - if !ok { - return false - } - n = *ptr - - ts, ok := n.(*ast.TypeSwitchStmt) - if !ok { - return false - } - - // Are any switch cases referring to reflect types? - // (That is, is this an old reflect type switch?) - for _, cas := range ts.Body.List { - for _, typ := range cas.(*ast.CaseClause).List { - if reflectType(typ) != "" { - goto haveReflect - } - } - } - return false - -haveReflect: - // Now we know it's an old reflect type switch. Prepare the new version, - // but don't replace or edit the original until we're sure of success. - - // Figure out the initializer statement, if any, and the receiver for the Kind call. - var init ast.Stmt - var rcvr ast.Expr - - init = ts.Init - switch n := ts.Assign.(type) { - default: - warn(ts.Pos(), "unexpected form in type switch") - return false - - case *ast.AssignStmt: - as := n - ta := as.Rhs[0].(*ast.TypeAssertExpr) - x := isIdent(as.Lhs[0]) - z := isIdent(ta.X) - - if isBlank(x) || x != nil && z != nil && x.Name == z.Name && !assignsTo(x, ts.Body.List) { - // Can drop the variable creation. - rcvr = ta.X - } else { - // Need to use initialization statement. - if init != nil { - warn(ts.Pos(), "cannot rewrite reflect type switch with initializing statement") - return false - } - init = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[0]}, - TokPos: as.TokPos, - Tok: token.DEFINE, - Rhs: []ast.Expr{ta.X}, - } - rcvr = as.Lhs[0] - } - - case *ast.ExprStmt: - rcvr = n.X.(*ast.TypeAssertExpr).X - } - - // Prepare rewritten type switch (see large comment above for form). - sw := &ast.SwitchStmt{ - Switch: ts.Switch, - Init: init, - Tag: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: rcvr, - Sel: &ast.Ident{ - NamePos: rcvr.End(), - Name: "Kind", - Obj: nil, - }, - }, - Lparen: rcvr.End(), - Rparen: rcvr.End(), - }, - Body: &ast.BlockStmt{ - Lbrace: ts.Body.Lbrace, - List: nil, // to be filled in - Rbrace: ts.Body.Rbrace, - }, - } - - // Translate cases. - for _, tcas := range ts.Body.List { - tcas := tcas.(*ast.CaseClause) - cas := &ast.CaseClause{ - Case: tcas.Case, - Colon: tcas.Colon, - Body: tcas.Body, - } - for _, t := range tcas.List { - if isTopName(t, "nil") { - cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", "Invalid")) - continue - } - - typ := reflectType(t) - if typ == "" { - warn(t.Pos(), "cannot rewrite reflect type switch case with non-reflect type %s", gofmt(t)) - cas.List = append(cas.List, t) - continue - } - - for _, k := range reflectKind[typ] { - cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", k)) - } - } - sw.Body.List = append(sw.Body.List, cas) - } - - // Everything worked. Rewrite AST. - *ptr = sw - return true -} - -// Rewrite x, y = z.(T) into -// x = z -// y = x.Kind() == K -// as described in the long comment above. -// -// If insert != nil, it can be called to insert a statement after *ptr in its block. -// If insert == nil, insertion is not possible. -// At most one call to insert is allowed. -// -// Scope gives the statements for which a declaration -// in *ptr would be in scope. -// -// The result is true of the statement was rewritten. -// -func reflectFixTypecheck(ptr *ast.Stmt, insert func(ast.Stmt), scope []ast.Stmt) bool { - st := *ptr - as, ok := st.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { - return false - } - - ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) - if !ok { - return false - } - typ := reflectType(ta.Type) - if typ == "" { - return false - } - - // Have x, y := z.(t). - x := isIdent(as.Lhs[0]) - y := isIdent(as.Lhs[1]) - z := isIdent(ta.X) - - // First step is x := z, unless it's x := x and the resulting x is never reassigned. - // rcvr is the x in x.Kind(). - var rcvr ast.Expr - if isBlank(x) || - as.Tok == token.DEFINE && x != nil && z != nil && x.Name == z.Name && !assignsTo(x, scope) { - // Can drop the statement. - // If we need to insert a statement later, now we have a slot. - *ptr = &ast.EmptyStmt{} - insert = func(x ast.Stmt) { *ptr = x } - rcvr = ta.X - } else { - *ptr = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[0]}, - TokPos: as.TokPos, - Tok: as.Tok, - Rhs: []ast.Expr{ta.X}, - } - rcvr = as.Lhs[0] - } - - // Prepare x.Kind() == T expression appropriate to t. - // If x is not a simple identifier, warn that we might be - // reevaluating x. - if x == nil { - warn(as.Pos(), "rewrite reevaluates expr with possible side effects: %s", gofmt(as.Lhs[0])) - } - yExpr, yNotExpr := reflectKindEq(rcvr, reflectKind[typ]) - - // Second step is y := x.Kind() == T, unless it's only used once - // or we have no way to insert that statement. - var yStmt *ast.AssignStmt - if as.Tok == token.DEFINE && countUses(y, scope) <= 1 || insert == nil { - // Can drop the statement and use the expression directly. - rewriteUses(y, - func(token.Pos) ast.Expr { return yExpr }, - func(token.Pos) ast.Expr { return yNotExpr }, - scope) - } else { - yStmt = &ast.AssignStmt{ - Lhs: []ast.Expr{as.Lhs[1]}, - TokPos: as.End(), - Tok: as.Tok, - Rhs: []ast.Expr{yExpr}, - } - insert(yStmt) - } - return true -} - -// reflectKindEq returns the expression z.Kind() == kinds[0] || z.Kind() == kinds[1] || ... -// and its negation. -// The qualifier "reflect." is inserted before each kinds[i] expression. -func reflectKindEq(z ast.Expr, kinds []string) (ast.Expr, ast.Expr) { - n := len(kinds) - if n == 1 { - y := &ast.BinaryExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: z, - Sel: ast.NewIdent("Kind"), - }, - }, - Op: token.EQL, - Y: newPkgDot(token.NoPos, "reflect", kinds[0]), - } - ynot := &ast.BinaryExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: z, - Sel: ast.NewIdent("Kind"), - }, - }, - Op: token.NEQ, - Y: newPkgDot(token.NoPos, "reflect", kinds[0]), - } - return y, ynot - } - - x, xnot := reflectKindEq(z, kinds[0:n-1]) - y, ynot := reflectKindEq(z, kinds[n-1:]) - - or := &ast.BinaryExpr{ - X: x, - Op: token.LOR, - Y: y, - } - andnot := &ast.BinaryExpr{ - X: xnot, - Op: token.LAND, - Y: ynot, - } - return or, andnot -} - -// if x represents a known old reflect type/value like *reflect.PtrType or reflect.ArrayOrSliceValue, -// reflectType returns the string form of that type. -func reflectType(x ast.Expr) string { - ptr, ok := x.(*ast.StarExpr) - if ok { - x = ptr.X - } - - sel, ok := x.(*ast.SelectorExpr) - if !ok || !isName(sel.X, "reflect") { - return "" - } - - var s = "reflect." - if ptr != nil { - s = "*reflect." - } - s += sel.Sel.Name - - if reflectKind[s] != nil { - return s - } - return "" -} - -// reflectWarnTypecheckStmt warns about statements -// of the form x, y = z.(T) for any old reflect type T. -// The last pass should have gotten them all, and if it didn't, -// the next pass is going to turn them into x, y = z. -func reflectWarnTypecheckStmt(n interface{}) { - as, ok := n.(*ast.AssignStmt) - if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { - return - } - ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) - if !ok || reflectType(ta.Type) == "" { - return - } - warn(n.(ast.Node).Pos(), "unfixed reflect type check") -} - -// reflectFixAssert rewrites x.(T) to x for any old reflect type T. -func reflectFixAssert(n interface{}) bool { - ptr, ok := n.(*ast.Expr) - if ok { - ta, ok := (*ptr).(*ast.TypeAssertExpr) - if ok && reflectType(ta.Type) != "" { - *ptr = ta.X - return true - } - } - return false -} - -// Tables describing the transformations. - -// Description of old reflect API for partial type checking. -// We pretend the Elem method is on Type and Value instead -// of enumerating all the types it is actually on. -// Also, we pretend that ArrayType etc embeds Type for the -// purposes of describing the API. (In fact they embed commonType, -// which implements Type.) -var reflectTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "reflect.ArrayOrSliceType": {Embed: []string{"reflect.Type"}}, - "reflect.ArrayOrSliceValue": {Embed: []string{"reflect.Value"}}, - "reflect.ArrayType": {Embed: []string{"reflect.Type"}}, - "reflect.ArrayValue": {Embed: []string{"reflect.Value"}}, - "reflect.BoolType": {Embed: []string{"reflect.Type"}}, - "reflect.BoolValue": {Embed: []string{"reflect.Value"}}, - "reflect.ChanType": {Embed: []string{"reflect.Type"}}, - "reflect.ChanValue": { - Method: map[string]string{ - "Recv": "func() (reflect.Value, bool)", - "TryRecv": "func() (reflect.Value, bool)", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.ComplexType": {Embed: []string{"reflect.Type"}}, - "reflect.ComplexValue": {Embed: []string{"reflect.Value"}}, - "reflect.FloatType": {Embed: []string{"reflect.Type"}}, - "reflect.FloatValue": {Embed: []string{"reflect.Value"}}, - "reflect.FuncType": { - Method: map[string]string{ - "In": "func(int) reflect.Type", - "Out": "func(int) reflect.Type", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.FuncValue": { - Method: map[string]string{ - "Call": "func([]reflect.Value) []reflect.Value", - }, - }, - "reflect.IntType": {Embed: []string{"reflect.Type"}}, - "reflect.IntValue": {Embed: []string{"reflect.Value"}}, - "reflect.InterfaceType": {Embed: []string{"reflect.Type"}}, - "reflect.InterfaceValue": {Embed: []string{"reflect.Value"}}, - "reflect.MapType": { - Method: map[string]string{ - "Key": "func() reflect.Type", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.MapValue": { - Method: map[string]string{ - "Keys": "func() []reflect.Value", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.Method": { - Field: map[string]string{ - "Type": "*reflect.FuncType", - "Func": "*reflect.FuncValue", - }, - }, - "reflect.PtrType": {Embed: []string{"reflect.Type"}}, - "reflect.PtrValue": {Embed: []string{"reflect.Value"}}, - "reflect.SliceType": {Embed: []string{"reflect.Type"}}, - "reflect.SliceValue": { - Method: map[string]string{ - "Slice": "func(int, int) *reflect.SliceValue", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.StringType": {Embed: []string{"reflect.Type"}}, - "reflect.StringValue": {Embed: []string{"reflect.Value"}}, - "reflect.StructField": { - Field: map[string]string{ - "Type": "reflect.Type", - }, - }, - "reflect.StructType": { - Method: map[string]string{ - "Field": "func() reflect.StructField", - "FieldByIndex": "func() reflect.StructField", - "FieldByName": "func() reflect.StructField,bool", - "FieldByNameFunc": "func() reflect.StructField,bool", - }, - Embed: []string{"reflect.Type"}, - }, - "reflect.StructValue": { - Method: map[string]string{ - "Field": "func() reflect.Value", - "FieldByIndex": "func() reflect.Value", - "FieldByName": "func() reflect.Value", - "FieldByNameFunc": "func() reflect.Value", - }, - Embed: []string{"reflect.Value"}, - }, - "reflect.Type": { - Method: map[string]string{ - "Elem": "func() reflect.Type", - "Method": "func() reflect.Method", - }, - }, - "reflect.UintType": {Embed: []string{"reflect.Type"}}, - "reflect.UintValue": {Embed: []string{"reflect.Value"}}, - "reflect.UnsafePointerType": {Embed: []string{"reflect.Type"}}, - "reflect.UnsafePointerValue": {Embed: []string{"reflect.Value"}}, - "reflect.Value": { - Method: map[string]string{ - "Addr": "func() *reflect.PtrValue", - "Elem": "func() reflect.Value", - "Method": "func() *reflect.FuncValue", - "SetValue": "func(reflect.Value)", - }, - }, - }, - Func: map[string]string{ - "reflect.Append": "*reflect.SliceValue", - "reflect.AppendSlice": "*reflect.SliceValue", - "reflect.Indirect": "reflect.Value", - "reflect.MakeSlice": "*reflect.SliceValue", - "reflect.MakeChan": "*reflect.ChanValue", - "reflect.MakeMap": "*reflect.MapValue", - "reflect.MakeZero": "reflect.Value", - "reflect.NewValue": "reflect.Value", - "reflect.PtrTo": "*reflect.PtrType", - "reflect.Typeof": "reflect.Type", - }, -} - -var reflectRewriteMethod = map[string]map[string]string{ - // The type API didn't change much. - "*reflect.ChanType": {"Dir": "ChanDir"}, - "*reflect.FuncType": {"DotDotDot": "IsVariadic"}, - - // The value API has longer names to disambiguate - // methods with different signatures. - "reflect.ArrayOrSliceValue": { // interface, not pointer - "Elem": "Index", - }, - "*reflect.ArrayValue": { - "Elem": "Index", - }, - "*reflect.BoolValue": { - "Get": "Bool", - "Set": "SetBool", - }, - "*reflect.ChanValue": { - "Get": "Pointer", - }, - "*reflect.ComplexValue": { - "Get": "Complex", - "Set": "SetComplex", - "Overflow": "OverflowComplex", - }, - "*reflect.FloatValue": { - "Get": "Float", - "Set": "SetFloat", - "Overflow": "OverflowFloat", - }, - "*reflect.FuncValue": { - "Get": "Pointer", - }, - "*reflect.IntValue": { - "Get": "Int", - "Set": "SetInt", - "Overflow": "OverflowInt", - }, - "*reflect.InterfaceValue": { - "Get": "InterfaceData", - }, - "*reflect.MapValue": { - "Elem": "MapIndex", - "Get": "Pointer", - "Keys": "MapKeys", - "SetElem": "SetMapIndex", - }, - "*reflect.PtrValue": { - "Get": "Pointer", - }, - "*reflect.SliceValue": { - "Elem": "Index", - "Get": "Pointer", - }, - "*reflect.StringValue": { - "Get": "String", - "Set": "SetString", - }, - "*reflect.UintValue": { - "Get": "Uint", - "Set": "SetUint", - "Overflow": "OverflowUint", - }, - "*reflect.UnsafePointerValue": { - "Get": "Pointer", - "Set": "SetPointer", - }, -} - -var reflectKind = map[string][]string{ - "reflect.ArrayOrSliceType": {"Array", "Slice"}, // interface, not pointer - "*reflect.ArrayType": {"Array"}, - "*reflect.BoolType": {"Bool"}, - "*reflect.ChanType": {"Chan"}, - "*reflect.ComplexType": {"Complex64", "Complex128"}, - "*reflect.FloatType": {"Float32", "Float64"}, - "*reflect.FuncType": {"Func"}, - "*reflect.IntType": {"Int", "Int8", "Int16", "Int32", "Int64"}, - "*reflect.InterfaceType": {"Interface"}, - "*reflect.MapType": {"Map"}, - "*reflect.PtrType": {"Ptr"}, - "*reflect.SliceType": {"Slice"}, - "*reflect.StringType": {"String"}, - "*reflect.StructType": {"Struct"}, - "*reflect.UintType": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, - "*reflect.UnsafePointerType": {"UnsafePointer"}, - - "reflect.ArrayOrSliceValue": {"Array", "Slice"}, // interface, not pointer - "*reflect.ArrayValue": {"Array"}, - "*reflect.BoolValue": {"Bool"}, - "*reflect.ChanValue": {"Chan"}, - "*reflect.ComplexValue": {"Complex64", "Complex128"}, - "*reflect.FloatValue": {"Float32", "Float64"}, - "*reflect.FuncValue": {"Func"}, - "*reflect.IntValue": {"Int", "Int8", "Int16", "Int32", "Int64"}, - "*reflect.InterfaceValue": {"Interface"}, - "*reflect.MapValue": {"Map"}, - "*reflect.PtrValue": {"Ptr"}, - "*reflect.SliceValue": {"Slice"}, - "*reflect.StringValue": {"String"}, - "*reflect.StructValue": {"Struct"}, - "*reflect.UintValue": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, - "*reflect.UnsafePointerValue": {"UnsafePointer"}, -} - -var isReflectValue = map[string]bool{ - "reflect.ArrayOrSliceValue": true, // interface, not pointer - "*reflect.ArrayValue": true, - "*reflect.BoolValue": true, - "*reflect.ChanValue": true, - "*reflect.ComplexValue": true, - "*reflect.FloatValue": true, - "*reflect.FuncValue": true, - "*reflect.IntValue": true, - "*reflect.InterfaceValue": true, - "*reflect.MapValue": true, - "*reflect.PtrValue": true, - "*reflect.SliceValue": true, - "*reflect.StringValue": true, - "*reflect.StructValue": true, - "*reflect.UintValue": true, - "*reflect.UnsafePointerValue": true, - "reflect.Value": true, // interface, not pointer -} diff --git a/src/cmd/fix/reflect_test.go b/src/cmd/fix/reflect_test.go deleted file mode 100644 index 032cbc745..000000000 --- a/src/cmd/fix/reflect_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file - -package main - -import ( - "io/ioutil" - "log" - "path/filepath" -) - -func init() { - addTestCases(reflectTests(), reflectFn) -} - -func reflectTests() []testCase { - var tests []testCase - - names, _ := filepath.Glob("testdata/reflect.*.in") - for _, in := range names { - out := in[:len(in)-len(".in")] + ".out" - inb, err := ioutil.ReadFile(in) - if err != nil { - log.Fatal(err) - } - outb, err := ioutil.ReadFile(out) - if err != nil { - log.Fatal(err) - } - tests = append(tests, testCase{Name: in, In: string(inb), Out: string(outb)}) - } - - return tests -} diff --git a/src/cmd/fix/signal.go b/src/cmd/fix/signal.go deleted file mode 100644 index 5a583d41e..000000000 --- a/src/cmd/fix/signal.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "strings" -) - -func init() { - register(signalFix) -} - -var signalFix = fix{ - "signal", - "2011-06-29", - signal, - `Adapt code to types moved from os/signal to signal. - -http://codereview.appspot.com/4437091 -`, -} - -func signal(f *ast.File) (fixed bool) { - if !imports(f, "os/signal") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - - if !ok || !isTopName(s.X, "signal") { - return - } - - sel := s.Sel.String() - if sel == "Signal" || sel == "UnixSignal" || strings.HasPrefix(sel, "SIG") { - addImport(f, "os") - s.X = &ast.Ident{Name: "os"} - fixed = true - } - }) - - if fixed && !usesImport(f, "os/signal") { - deleteImport(f, "os/signal") - } - return -} diff --git a/src/cmd/fix/signal_test.go b/src/cmd/fix/signal_test.go deleted file mode 100644 index 7bca7d5c4..000000000 --- a/src/cmd/fix/signal_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(signalTests, signal) -} - -var signalTests = []testCase{ - { - Name: "signal.0", - In: `package main - -import ( - _ "a" - "os/signal" - _ "z" -) - -type T1 signal.UnixSignal -type T2 signal.Signal - -func f() { - _ = signal.SIGHUP - _ = signal.Incoming -} -`, - Out: `package main - -import ( - _ "a" - "os" - "os/signal" - _ "z" -) - -type T1 os.UnixSignal -type T2 os.Signal - -func f() { - _ = os.SIGHUP - _ = signal.Incoming -} -`, - }, - { - Name: "signal.1", - In: `package main - -import ( - "os" - "os/signal" -) - -func f() { - var _ os.Error - _ = signal.SIGHUP -} -`, - Out: `package main - -import "os" - -func f() { - var _ os.Error - _ = os.SIGHUP -} -`, - }, - { - Name: "signal.2", - In: `package main - -import "os" -import "os/signal" - -func f() { - var _ os.Error - _ = signal.SIGHUP -} -`, - Out: `package main - -import "os" - -func f() { - var _ os.Error - _ = os.SIGHUP -} -`, - }, -} diff --git a/src/cmd/fix/sorthelpers.go b/src/cmd/fix/sorthelpers.go deleted file mode 100644 index fa549313e..000000000 --- a/src/cmd/fix/sorthelpers.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(sorthelpersFix) -} - -var sorthelpersFix = fix{ - "sorthelpers", - "2011-07-08", - sorthelpers, - `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings]. -`, -} - -func sorthelpers(f *ast.File) (fixed bool) { - if !imports(f, "sort") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if !ok || !isTopName(s.X, "sort") { - return - } - - switch s.Sel.String() { - case "SortFloat64s": - s.Sel.Name = "Float64s" - case "SortInts": - s.Sel.Name = "Ints" - case "SortStrings": - s.Sel.Name = "Strings" - default: - return - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/sorthelpers_test.go b/src/cmd/fix/sorthelpers_test.go deleted file mode 100644 index dd6b58e03..000000000 --- a/src/cmd/fix/sorthelpers_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(sorthelpersTests, sorthelpers) -} - -var sorthelpersTests = []testCase{ - { - Name: "sortslice.0", - In: `package main - -import ( - "sort" -) - -func main() { - var s []string - sort.SortStrings(s) - var i []ints - sort.SortInts(i) - var f []float64 - sort.SortFloat64s(f) -} -`, - Out: `package main - -import ( - "sort" -) - -func main() { - var s []string - sort.Strings(s) - var i []ints - sort.Ints(i) - var f []float64 - sort.Float64s(f) -} -`, - }, -} diff --git a/src/cmd/fix/sortslice.go b/src/cmd/fix/sortslice.go deleted file mode 100644 index 89267b847..000000000 --- a/src/cmd/fix/sortslice.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(sortsliceFix) -} - -var sortsliceFix = fix{ - "sortslice", - "2011-06-26", - sortslice, - `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice. - -http://codereview.appspot.com/4602054 -http://codereview.appspot.com/4639041 -`, -} - -func sortslice(f *ast.File) (fixed bool) { - if !imports(f, "sort") { - return - } - - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if !ok || !isTopName(s.X, "sort") { - return - } - - switch s.Sel.String() { - case "Float64Array": - s.Sel.Name = "Float64Slice" - case "IntArray": - s.Sel.Name = "IntSlice" - case "StringArray": - s.Sel.Name = "StringSlice" - default: - return - } - - fixed = true - }) - - return -} diff --git a/src/cmd/fix/sortslice_test.go b/src/cmd/fix/sortslice_test.go deleted file mode 100644 index 7b745a232..000000000 --- a/src/cmd/fix/sortslice_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(sortsliceTests, sortslice) -} - -var sortsliceTests = []testCase{ - { - Name: "sortslice.0", - In: `package main - -import ( - "sort" -) - -var _ = sort.Float64Array -var _ = sort.IntArray -var _ = sort.StringArray -`, - Out: `package main - -import ( - "sort" -) - -var _ = sort.Float64Slice -var _ = sort.IntSlice -var _ = sort.StringSlice -`, - }, -} diff --git a/src/cmd/fix/strconv.go b/src/cmd/fix/strconv.go deleted file mode 100644 index 6cd69020b..000000000 --- a/src/cmd/fix/strconv.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(strconvFix) -} - -var strconvFix = fix{ - "strconv", - "2011-12-01", - strconvFn, - `Convert to new strconv API. - -http://codereview.appspot.com/5434095 -http://codereview.appspot.com/5434069 -`, -} - -func strconvFn(f *ast.File) bool { - if !imports(f, "strconv") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions. - call, ok := n.(*ast.CallExpr) - if !ok || len(call.Args) < 1 { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || !isTopName(sel.X, "strconv") { - return - } - change := func(name string) { - fixed = true - sel.Sel.Name = name - } - add := func(s string) { - call.Args = append(call.Args, expr(s)) - } - switch sel.Sel.Name { - case "Atob": - change("ParseBool") - case "Atof32": - change("ParseFloat") - add("32") // bitSize - warn(call.Pos(), "rewrote strconv.Atof32(_) to strconv.ParseFloat(_, 32) but return value must be converted to float32") - case "Atof64": - change("ParseFloat") - add("64") // bitSize - case "AtofN": - change("ParseFloat") - case "Atoi": - // Atoi stayed as a convenience wrapper. - case "Atoi64": - change("ParseInt") - add("10") // base - add("64") // bitSize - case "Atoui": - change("ParseUint") - add("10") // base - add("0") // bitSize - warn(call.Pos(), "rewrote strconv.Atoui(_) to strconv.ParseUint(_, 10, 0) but return value must be converted to uint") - case "Atoui64": - change("ParseUint") - add("10") // base - add("64") // bitSize - case "Btoa": - change("FormatBool") - case "Btoi64": - change("ParseInt") - add("64") // bitSize - case "Btoui64": - change("ParseUint") - add("64") // bitSize - case "Ftoa32": - change("FormatFloat") - call.Args[0] = strconvRewrite("float32", "float64", call.Args[0]) - add("32") // bitSize - case "Ftoa64": - change("FormatFloat") - add("64") // bitSize - case "FtoaN": - change("FormatFloat") - case "Itoa": - // Itoa stayed as a convenience wrapper. - case "Itoa64": - change("FormatInt") - add("10") // base - case "Itob": - change("FormatInt") - call.Args[0] = strconvRewrite("int", "int64", call.Args[0]) - case "Itob64": - change("FormatInt") - case "Uitoa": - change("FormatUint") - call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) - add("10") // base - case "Uitoa64": - change("FormatUint") - add("10") // base - case "Uitob": - change("FormatUint") - call.Args[0] = strconvRewrite("uint", "uint64", call.Args[0]) - case "Uitob64": - change("FormatUint") - } - }) - return fixed -} - -// rewrite from type t1 to type t2 -// If the expression x is of the form t1(_), use t2(_). Otherwise use t2(x). -func strconvRewrite(t1, t2 string, x ast.Expr) ast.Expr { - if call, ok := x.(*ast.CallExpr); ok && isTopName(call.Fun, t1) { - call.Fun.(*ast.Ident).Name = t2 - return x - } - return &ast.CallExpr{Fun: ast.NewIdent(t2), Args: []ast.Expr{x}} -} diff --git a/src/cmd/fix/strconv_test.go b/src/cmd/fix/strconv_test.go deleted file mode 100644 index 7fbd4e42e..000000000 --- a/src/cmd/fix/strconv_test.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(strconvTests, strconvFn) -} - -var strconvTests = []testCase{ - { - Name: "strconv.0", - In: `package main - -import "strconv" - -func f() { - foo.Atob("abc") - - strconv.Atob("true") - strconv.Btoa(false) - - strconv.Atof32("1.2") - strconv.Atof64("1.2") - strconv.AtofN("1.2", 64) - strconv.Ftoa32(1.2, 'g', 17) - strconv.Ftoa64(1.2, 'g', 17) - strconv.FtoaN(1.2, 'g', 17, 64) - - strconv.Atoi("3") - strconv.Atoi64("3") - strconv.Btoi64("1234", 5) - - strconv.Atoui("3") - strconv.Atoui64("3") - strconv.Btoui64("1234", 5) - - strconv.Itoa(123) - strconv.Itoa64(1234) - strconv.Itob(123, 5) - strconv.Itob64(1234, 5) - - strconv.Uitoa(123) - strconv.Uitoa64(1234) - strconv.Uitob(123, 5) - strconv.Uitob64(1234, 5) - - strconv.Uitoa(uint(x)) - strconv.Uitoa(f(x)) -} -`, - Out: `package main - -import "strconv" - -func f() { - foo.Atob("abc") - - strconv.ParseBool("true") - strconv.FormatBool(false) - - strconv.ParseFloat("1.2", 32) - strconv.ParseFloat("1.2", 64) - strconv.ParseFloat("1.2", 64) - strconv.FormatFloat(float64(1.2), 'g', 17, 32) - strconv.FormatFloat(1.2, 'g', 17, 64) - strconv.FormatFloat(1.2, 'g', 17, 64) - - strconv.Atoi("3") - strconv.ParseInt("3", 10, 64) - strconv.ParseInt("1234", 5, 64) - - strconv.ParseUint("3", 10, 0) - strconv.ParseUint("3", 10, 64) - strconv.ParseUint("1234", 5, 64) - - strconv.Itoa(123) - strconv.FormatInt(1234, 10) - strconv.FormatInt(int64(123), 5) - strconv.FormatInt(1234, 5) - - strconv.FormatUint(uint64(123), 10) - strconv.FormatUint(1234, 10) - strconv.FormatUint(uint64(123), 5) - strconv.FormatUint(1234, 5) - - strconv.FormatUint(uint64(x), 10) - strconv.FormatUint(uint64(f(x)), 10) -} -`, - }, -} diff --git a/src/cmd/fix/stringssplit.go b/src/cmd/fix/stringssplit.go deleted file mode 100644 index d89ecf039..000000000 --- a/src/cmd/fix/stringssplit.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" -) - -func init() { - register(stringssplitFix) -} - -var stringssplitFix = fix{ - "stringssplit", - "2011-06-28", - stringssplit, - `Restore strings.Split to its original meaning and add strings.SplitN. Bytes too. - -http://codereview.appspot.com/4661051 -`, -} - -func stringssplit(f *ast.File) bool { - if !imports(f, "bytes") && !imports(f, "strings") { - return false - } - - fixed := false - walk(f, func(n interface{}) { - call, ok := n.(*ast.CallExpr) - // func Split(s, sep string, n int) []string - // func SplitAfter(s, sep string, n int) []string - if !ok || len(call.Args) != 3 { - return - } - // Is this our function? - switch { - case isPkgDot(call.Fun, "bytes", "Split"): - case isPkgDot(call.Fun, "bytes", "SplitAfter"): - case isPkgDot(call.Fun, "strings", "Split"): - case isPkgDot(call.Fun, "strings", "SplitAfter"): - default: - return - } - - sel := call.Fun.(*ast.SelectorExpr) - args := call.Args - fixed = true // We're committed. - - // Is the last argument -1? If so, drop the arg. - // (Actually we just look for a negative integer literal.) - // Otherwise, Split->SplitN and keep the arg. - final := args[2] - if unary, ok := final.(*ast.UnaryExpr); ok && unary.Op == token.SUB { - if lit, ok := unary.X.(*ast.BasicLit); ok { - // Is it an integer? If so, it's a negative integer and that's what we're after. - if lit.Kind == token.INT { - // drop the last arg. - call.Args = args[0:2] - return - } - } - } - - // If not, rename and keep the argument list. - sel.Sel.Name += "N" - }) - return fixed -} diff --git a/src/cmd/fix/stringssplit_test.go b/src/cmd/fix/stringssplit_test.go deleted file mode 100644 index fa42b1bea..000000000 --- a/src/cmd/fix/stringssplit_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(stringssplitTests, stringssplit) -} - -var stringssplitTests = []testCase{ - { - Name: "stringssplit.0", - In: `package main - -import ( - "bytes" - "strings" -) - -func f() { - bytes.Split(a, b, c) - bytes.Split(a, b, -1) - bytes.SplitAfter(a, b, c) - bytes.SplitAfter(a, b, -1) - strings.Split(a, b, c) - strings.Split(a, b, -1) - strings.SplitAfter(a, b, c) - strings.SplitAfter(a, b, -1) -} -`, - Out: `package main - -import ( - "bytes" - "strings" -) - -func f() { - bytes.SplitN(a, b, c) - bytes.Split(a, b) - bytes.SplitAfterN(a, b, c) - bytes.SplitAfter(a, b) - strings.SplitN(a, b, c) - strings.Split(a, b) - strings.SplitAfterN(a, b, c) - strings.SplitAfter(a, b) -} -`, - }, -} diff --git a/src/cmd/fix/template.go b/src/cmd/fix/template.go deleted file mode 100644 index a3dd1440b..000000000 --- a/src/cmd/fix/template.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" -) - -func init() { - register(templateFix) -} - -var templateFix = fix{ - "template", - "2011-11-22", - template, - `Rewrite calls to template.ParseFile to template.ParseFiles - -http://codereview.appspot.com/5433048 -`, -} - -var templateSetGlobals = []string{ - "ParseSetFiles", - "ParseSetGlob", - "ParseTemplateFiles", - "ParseTemplateGlob", - "Set", - "SetMust", -} - -var templateSetMethods = []string{ - "ParseSetFiles", - "ParseSetGlob", - "ParseTemplateFiles", - "ParseTemplateGlob", -} - -var templateTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "template.Template": { - Method: map[string]string{ - "Funcs": "func() *template.Template", - "Delims": "func() *template.Template", - "Parse": "func() (*template.Template, error)", - "ParseFile": "func() (*template.Template, error)", - "ParseInSet": "func() (*template.Template, error)", - }, - }, - "template.Set": { - Method: map[string]string{ - "ParseSetFiles": "func() (*template.Set, error)", - "ParseSetGlob": "func() (*template.Set, error)", - "ParseTemplateFiles": "func() (*template.Set, error)", - "ParseTemplateGlob": "func() (*template.Set, error)", - }, - }, - }, - - Func: map[string]string{ - "template.New": "*template.Template", - "template.Must": "(*template.Template, error)", - "template.SetMust": "(*template.Set, error)", - }, -} - -func template(f *ast.File) bool { - if !imports(f, "text/template") && !imports(f, "html/template") { - return false - } - - fixed := false - - typeof, _ := typecheck(templateTypeConfig, f) - - // Now update the names used by importers. - walk(f, func(n interface{}) { - if sel, ok := n.(*ast.SelectorExpr); ok { - // Reference to top-level function ParseFile. - if isPkgDot(sel, "template", "ParseFile") { - sel.Sel.Name = "ParseFiles" - fixed = true - return - } - // Reference to ParseFiles method. - if typeof[sel.X] == "*template.Template" && sel.Sel.Name == "ParseFile" { - sel.Sel.Name = "ParseFiles" - fixed = true - return - } - // The Set type and its functions are now gone. - for _, name := range templateSetGlobals { - if isPkgDot(sel, "template", name) { - warn(sel.Pos(), "reference to template.%s must be fixed manually", name) - return - } - } - // The methods of Set are now gone. - for _, name := range templateSetMethods { - if typeof[sel.X] == "*template.Set" && sel.Sel.Name == name { - warn(sel.Pos(), "reference to template.*Set.%s must be fixed manually", name) - return - } - } - } - }) - - return fixed -} diff --git a/src/cmd/fix/template_test.go b/src/cmd/fix/template_test.go deleted file mode 100644 index f713a2901..000000000 --- a/src/cmd/fix/template_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(templateTests, template) -} - -var templateTests = []testCase{ - { - Name: "template.0", - In: `package main - -import ( - "text/template" -) - -func f() { - template.ParseFile(a) - var t template.Template - x, y := template.ParseFile() - template.New("x").Funcs(m).ParseFile(a) // chained method - // Output should complain about these as functions or methods. - var s *template.Set - s.ParseSetFiles(a) - template.ParseSetGlob(a) - s.ParseTemplateFiles(a) - template.ParseTemplateGlob(a) - x := template.SetMust(a()) -} -`, - Out: `package main - -import ( - "text/template" -) - -func f() { - template.ParseFiles(a) - var t template.Template - x, y := template.ParseFiles() - template.New("x").Funcs(m).ParseFiles(a) // chained method - // Output should complain about these as functions or methods. - var s *template.Set - s.ParseSetFiles(a) - template.ParseSetGlob(a) - s.ParseTemplateFiles(a) - template.ParseTemplateGlob(a) - x := template.SetMust(a()) -} -`, - }, -} diff --git a/src/cmd/fix/testdata/reflect.encoder.go.in b/src/cmd/fix/testdata/reflect.encoder.go.in index 0202d79ac..702f6dc06 100644 --- a/src/cmd/fix/testdata/reflect.encoder.go.in +++ b/src/cmd/fix/testdata/reflect.encoder.go.in @@ -120,7 +120,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp return true } -// sendType sends the type info to the other side, if necessary. +// sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) if ut.isGobEncoder { diff --git a/src/cmd/fix/testdata/reflect.encoder.go.out b/src/cmd/fix/testdata/reflect.encoder.go.out index 925d39301..f1a7b98f1 100644 --- a/src/cmd/fix/testdata/reflect.encoder.go.out +++ b/src/cmd/fix/testdata/reflect.encoder.go.out @@ -120,7 +120,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp return true } -// sendType sends the type info to the other side, if necessary. +// sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) if ut.isGobEncoder { diff --git a/src/cmd/fix/testdata/reflect.export.go.in b/src/cmd/fix/testdata/reflect.export.go.in index ce7940b29..722387ac5 100644 --- a/src/cmd/fix/testdata/reflect.export.go.in +++ b/src/cmd/fix/testdata/reflect.export.go.in @@ -162,7 +162,7 @@ func (client *expClient) run() { // all messages <=N have been seen by the recipient. We check anyway. expLog("sequence out of order:", client.ackNum, hdr.SeqNum) } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. + if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. client.ackNum = hdr.SeqNum } client.mu.Unlock() diff --git a/src/cmd/fix/testdata/reflect.export.go.out b/src/cmd/fix/testdata/reflect.export.go.out index 7bd73c5e7..d1324f346 100644 --- a/src/cmd/fix/testdata/reflect.export.go.out +++ b/src/cmd/fix/testdata/reflect.export.go.out @@ -162,7 +162,7 @@ func (client *expClient) run() { // all messages <=N have been seen by the recipient. We check anyway. expLog("sequence out of order:", client.ackNum, hdr.SeqNum) } - if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. + if client.ackNum < hdr.SeqNum { // If there has been an error, don't back up the count. client.ackNum = hdr.SeqNum } client.mu.Unlock() diff --git a/src/cmd/fix/testdata/reflect.print.go.in b/src/cmd/fix/testdata/reflect.print.go.in index 6c9b8e4f9..14cf2b215 100644 --- a/src/cmd/fix/testdata/reflect.print.go.in +++ b/src/cmd/fix/testdata/reflect.print.go.in @@ -182,7 +182,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { return os.NewError(Sprintf(format, a...)) diff --git a/src/cmd/fix/testdata/reflect.print.go.out b/src/cmd/fix/testdata/reflect.print.go.out index b475a2ae1..e4e4c7368 100644 --- a/src/cmd/fix/testdata/reflect.print.go.out +++ b/src/cmd/fix/testdata/reflect.print.go.out @@ -182,7 +182,7 @@ func Sprintf(format string, a ...interface{}) string { return s } -// Errorf formats according to a format specifier and returns the string +// Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { return os.NewError(Sprintf(format, a...)) diff --git a/src/cmd/fix/timefileinfo.go b/src/cmd/fix/timefileinfo.go deleted file mode 100644 index b2ea23d8f..000000000 --- a/src/cmd/fix/timefileinfo.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "go/ast" - "go/token" - "strings" -) - -func init() { - register(timefileinfoFix) -} - -var timefileinfoFix = fix{ - "time+fileinfo", - "2011-11-29", - timefileinfo, - `Rewrite for new time and os.FileInfo APIs. - -This fix applies some of the more mechanical changes, -but most code will still need manual cleanup. - -http://codereview.appspot.com/5392041 -http://codereview.appspot.com/5416060 -`, -} - -var timefileinfoTypeConfig = &TypeConfig{ - Type: map[string]*Type{ - "os.File": { - Method: map[string]string{ - "Readdir": "func() []*os.FileInfo", - "Stat": "func() (*os.FileInfo, error)", - }, - }, - "time.Time": { - Method: map[string]string{ - "Seconds": "time.raw", - "Nanoseconds": "time.raw", - }, - }, - }, - Func: map[string]string{ - "ioutil.ReadDir": "([]*os.FileInfo, error)", - "os.Stat": "(*os.FileInfo, error)", - "os.Lstat": "(*os.FileInfo, error)", - "time.LocalTime": "*time.Time", - "time.UTC": "*time.Time", - "time.SecondsToLocalTime": "*time.Time", - "time.SecondsToUTC": "*time.Time", - "time.NanosecondsToLocalTime": "*time.Time", - "time.NanosecondsToUTC": "*time.Time", - "time.Parse": "(*time.Time, error)", - "time.Nanoseconds": "time.raw", - "time.Seconds": "time.raw", - }, -} - -// timefileinfoIsOld reports whether f has evidence of being -// "old code", from before the API changes. Evidence means: -// -// a mention of *os.FileInfo (the pointer) -// a mention of *time.Time (the pointer) -// a mention of old functions from package time -// an attempt to call time.UTC -// -func timefileinfoIsOld(f *ast.File, typeof map[interface{}]string) bool { - old := false - - // called records the expressions that appear as - // the function part of a function call, so that - // we can distinguish a ref to the possibly new time.UTC - // from the definitely old time.UTC() function call. - called := make(map[interface{}]bool) - - before := func(n interface{}) { - if old { - return - } - if star, ok := n.(*ast.StarExpr); ok { - if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") { - old = true - return - } - } - if sel, ok := n.(*ast.SelectorExpr); ok { - if isTopName(sel.X, "time") { - if timefileinfoOldTimeFunc[sel.Sel.Name] { - old = true - return - } - } - if typeof[sel.X] == "os.FileInfo" || typeof[sel.X] == "*os.FileInfo" { - switch sel.Sel.Name { - case "Mtime_ns", "IsDirectory", "IsRegular": - old = true - return - case "Name", "Mode", "Size": - if !called[sel] { - old = true - return - } - } - } - } - call, ok := n.(*ast.CallExpr) - if ok && isPkgDot(call.Fun, "time", "UTC") { - old = true - return - } - if ok { - called[call.Fun] = true - } - } - walkBeforeAfter(f, before, nop) - return old -} - -var timefileinfoOldTimeFunc = map[string]bool{ - "LocalTime": true, - "SecondsToLocalTime": true, - "SecondsToUTC": true, - "NanosecondsToLocalTime": true, - "NanosecondsToUTC": true, - "Seconds": true, - "Nanoseconds": true, -} - -var isTimeNow = map[string]bool{ - "LocalTime": true, - "UTC": true, - "Seconds": true, - "Nanoseconds": true, -} - -func timefileinfo(f *ast.File) bool { - if !imports(f, "os") && !imports(f, "time") && !imports(f, "io/ioutil") { - return false - } - - typeof, _ := typecheck(timefileinfoTypeConfig, f) - - if !timefileinfoIsOld(f, typeof) { - return false - } - - fixed := false - walk(f, func(n interface{}) { - p, ok := n.(*ast.Expr) - if !ok { - return - } - nn := *p - - // Rewrite *os.FileInfo and *time.Time to drop the pointer. - if star, ok := nn.(*ast.StarExpr); ok { - if isPkgDot(star.X, "os", "FileInfo") || isPkgDot(star.X, "time", "Time") { - fixed = true - *p = star.X - return - } - } - - // Rewrite old time API calls to new calls. - // The code will still not compile after this edit, - // but the compiler will catch that, and the replacement - // code will be the correct functions to use in the new API. - if sel, ok := nn.(*ast.SelectorExpr); ok && isTopName(sel.X, "time") { - fn := sel.Sel.Name - if fn == "LocalTime" || fn == "Seconds" || fn == "Nanoseconds" { - fixed = true - sel.Sel.Name = "Now" - return - } - } - - if call, ok := nn.(*ast.CallExpr); ok { - if sel, ok := call.Fun.(*ast.SelectorExpr); ok { - // Rewrite time.UTC but only when called (there's a new time.UTC var now). - if isPkgDot(sel, "time", "UTC") { - fixed = true - sel.Sel.Name = "Now" - // rewrite time.Now() into time.Now().UTC() - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: call, - Sel: ast.NewIdent("UTC"), - }, - } - return - } - - // Rewrite conversions. - if ok && isTopName(sel.X, "time") && len(call.Args) == 1 { - fn := sel.Sel.Name - switch fn { - case "SecondsToLocalTime", "SecondsToUTC", - "NanosecondsToLocalTime", "NanosecondsToUTC": - fixed = true - sel.Sel.Name = "Unix" - call.Args = append(call.Args, nil) - if strings.HasPrefix(fn, "Seconds") { - // Unix(sec, 0) - call.Args[1] = ast.NewIdent("0") - } else { - // Unix(0, nsec) - call.Args[1] = call.Args[0] - call.Args[0] = ast.NewIdent("0") - } - if strings.HasSuffix(fn, "ToUTC") { - // rewrite call into call.UTC() - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: call, - Sel: ast.NewIdent("UTC"), - }, - } - } - return - } - } - - // Rewrite method calls. - switch typeof[sel.X] { - case "*time.Time", "time.Time": - switch sel.Sel.Name { - case "Seconds": - fixed = true - sel.Sel.Name = "Unix" - return - case "Nanoseconds": - fixed = true - sel.Sel.Name = "UnixNano" - return - } - - case "*os.FileInfo", "os.FileInfo": - switch sel.Sel.Name { - case "IsDirectory": - fixed = true - sel.Sel.Name = "IsDir" - return - case "IsRegular": - fixed = true - sel.Sel.Name = "IsDir" - *p = &ast.UnaryExpr{ - Op: token.NOT, - X: call, - } - return - } - } - } - } - - // Rewrite subtraction of two times. - // Cannot handle +=/-=. - if bin, ok := nn.(*ast.BinaryExpr); ok && - bin.Op == token.SUB && - (typeof[bin.X] == "time.raw" || typeof[bin.Y] == "time.raw") { - fixed = true - *p = &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: bin.X, - Sel: ast.NewIdent("Sub"), - }, - Args: []ast.Expr{bin.Y}, - } - } - - // Rewrite field references for os.FileInfo. - if sel, ok := nn.(*ast.SelectorExpr); ok { - if typ := typeof[sel.X]; typ == "*os.FileInfo" || typ == "os.FileInfo" { - addCall := false - switch sel.Sel.Name { - case "Name", "Size", "Mode": - fixed = true - addCall = true - case "Mtime_ns": - fixed = true - sel.Sel.Name = "ModTime" - addCall = true - } - if addCall { - *p = &ast.CallExpr{ - Fun: sel, - } - return - } - } - } - }) - - return true -} diff --git a/src/cmd/fix/timefileinfo_test.go b/src/cmd/fix/timefileinfo_test.go deleted file mode 100644 index 6573b8545..000000000 --- a/src/cmd/fix/timefileinfo_test.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(timefileinfoTests, timefileinfo) -} - -var timefileinfoTests = []testCase{ - { - Name: "timefileinfo.0", - In: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Name -} -`, - Out: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Name() -} -`, - }, - { - Name: "timefileinfo.1", - In: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Size - _ = st.Mode - _ = st.Mtime_ns - _ = st.IsDirectory() - _ = st.IsRegular() -} -`, - Out: `package main - -import "os" - -func main() { - st, _ := os.Stat("/etc/passwd") - _ = st.Size() - _ = st.Mode() - _ = st.ModTime() - _ = st.IsDir() - _ = !st.IsDir() -} -`, - }, - { - Name: "timefileinfo.2", - In: `package main - -import "os" - -func f(st *os.FileInfo) { - _ = st.Name - _ = st.Size - _ = st.Mode - _ = st.Mtime_ns - _ = st.IsDirectory() - _ = st.IsRegular() -} -`, - Out: `package main - -import "os" - -func f(st os.FileInfo) { - _ = st.Name() - _ = st.Size() - _ = st.Mode() - _ = st.ModTime() - _ = st.IsDir() - _ = !st.IsDir() -} -`, - }, - { - Name: "timefileinfo.3", - In: `package main - -import "time" - -func main() { - _ = time.Seconds() - _ = time.Nanoseconds() - _ = time.LocalTime() - _ = time.UTC() - _ = time.SecondsToLocalTime(sec) - _ = time.SecondsToUTC(sec) - _ = time.NanosecondsToLocalTime(nsec) - _ = time.NanosecondsToUTC(nsec) -} -`, - Out: `package main - -import "time" - -func main() { - _ = time.Now() - _ = time.Now() - _ = time.Now() - _ = time.Now().UTC() - _ = time.Unix(sec, 0) - _ = time.Unix(sec, 0).UTC() - _ = time.Unix(0, nsec) - _ = time.Unix(0, nsec).UTC() -} -`, - }, - { - Name: "timefileinfo.4", - In: `package main - -import "time" - -func f(*time.Time) - -func main() { - t := time.LocalTime() - _ = t.Seconds() - _ = t.Nanoseconds() - - t1 := time.Nanoseconds() - f(nil) - t2 := time.Nanoseconds() - dt := t2 - t1 -} -`, - Out: `package main - -import "time" - -func f(time.Time) - -func main() { - t := time.Now() - _ = t.Unix() - _ = t.UnixNano() - - t1 := time.Now() - f(nil) - t2 := time.Now() - dt := t2.Sub(t1) -} -`, - }, - { - Name: "timefileinfo.5", // test for issues 1505, 2636 - In: `package main - -import ( - "fmt" - "time" -) - -func main() { - fmt.Println(time.SecondsToUTC(now)) // this comment must not introduce an illegal linebreak -} -`, - Out: `package main - -import ( - "fmt" - "time" -) - -func main() { - fmt.Println(time.Unix(now, 0).UTC( // this comment must not introduce an illegal linebreak - )) -} -`, - }, -} diff --git a/src/cmd/fix/typecheck.go b/src/cmd/fix/typecheck.go index 8e54314d1..d33b69fdd 100644 --- a/src/cmd/fix/typecheck.go +++ b/src/cmd/fix/typecheck.go @@ -17,7 +17,7 @@ import ( // // The fact that it is partial is very important: the input is // an AST and a description of some type information to -// assume about one or more packages, but not all the +// assume about one or more packages, but not all the // packages that the program imports. The checker is // expected to do as much as it can with what it has been // given. There is not enough information supplied to do @@ -395,9 +395,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a // Field or method. name := n.Sel.Name if t := typeof[n.X]; t != "" { - if strings.HasPrefix(t, "*") { - t = t[1:] // implicit * - } + t = strings.TrimPrefix(t, "*") // implicit * if typ := cfg.Type[t]; typ != nil { if t := typ.dot(cfg, name); t != "" { typeof[n] = t diff --git a/src/cmd/fix/url.go b/src/cmd/fix/url.go deleted file mode 100644 index 49aac739b..000000000 --- a/src/cmd/fix/url.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(urlFix) -} - -var urlFix = fix{ - "url", - "2011-08-17", - url, - `Move the URL pieces of package http into a new package, url. - -http://codereview.appspot.com/4893043 -`, -} - -var urlRenames = []struct{ in, out string }{ - {"URL", "URL"}, - {"ParseURL", "Parse"}, - {"ParseURLReference", "ParseWithReference"}, - {"ParseQuery", "ParseQuery"}, - {"Values", "Values"}, - {"URLEscape", "QueryEscape"}, - {"URLUnescape", "QueryUnescape"}, - {"URLError", "Error"}, - {"URLEscapeError", "EscapeError"}, -} - -func url(f *ast.File) bool { - if imports(f, "url") || !imports(f, "http") { - return false - } - - fixed := false - - // Update URL code. - urlWalk := func(n interface{}) { - // Is it an identifier? - if ident, ok := n.(*ast.Ident); ok && ident.Name == "url" { - ident.Name = "url_" - return - } - // Parameter and result names. - if fn, ok := n.(*ast.FuncType); ok { - fixed = urlDoFields(fn.Params) || fixed - fixed = urlDoFields(fn.Results) || fixed - } - } - - // Fix up URL code and add import, at most once. - fix := func() { - if fixed { - return - } - addImport(f, "url") - walkBeforeAfter(f, urlWalk, nop) - fixed = true - } - - walk(f, func(n interface{}) { - // Rename functions and methods. - if expr, ok := n.(ast.Expr); ok { - for _, s := range urlRenames { - if isPkgDot(expr, "http", s.in) { - fix() - expr.(*ast.SelectorExpr).X.(*ast.Ident).Name = "url" - expr.(*ast.SelectorExpr).Sel.Name = s.out - return - } - } - } - }) - - // Remove the http import if no longer needed. - if fixed && !usesImport(f, "http") { - deleteImport(f, "http") - } - - return fixed -} - -func urlDoFields(list *ast.FieldList) (fixed bool) { - if list == nil { - return - } - for _, field := range list.List { - for _, ident := range field.Names { - if ident.Name == "url" { - fixed = true - ident.Name = "url_" - } - } - } - return -} diff --git a/src/cmd/fix/url2.go b/src/cmd/fix/url2.go deleted file mode 100644 index 5fd05ad2a..000000000 --- a/src/cmd/fix/url2.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import "go/ast" - -func init() { - register(url2Fix) -} - -var url2Fix = fix{ - "url2", - "2012-02-16", - url2, - `Rename some functions in net/url. - -http://codereview.appspot.com/5671061 -`, -} - -func url2(f *ast.File) bool { - if !imports(f, "net/url") { - return false - } - - fixed := false - - walk(f, func(n interface{}) { - // Rename functions and methods. - sel, ok := n.(*ast.SelectorExpr) - if !ok { - return - } - if !isTopName(sel.X, "url") { - return - } - if sel.Sel.Name == "ParseWithReference" { - sel.Sel.Name = "ParseWithFragment" - fixed = true - } - }) - - return fixed -} diff --git a/src/cmd/fix/url2_test.go b/src/cmd/fix/url2_test.go deleted file mode 100644 index c68dd88f1..000000000 --- a/src/cmd/fix/url2_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(url2Tests, url2) -} - -var url2Tests = []testCase{ - { - Name: "url2.0", - In: `package main - -import "net/url" - -func f() { - url.ParseWithReference("foo") -} -`, - Out: `package main - -import "net/url" - -func f() { - url.ParseWithFragment("foo") -} -`, - }, -} diff --git a/src/cmd/fix/url_test.go b/src/cmd/fix/url_test.go deleted file mode 100644 index 39827f780..000000000 --- a/src/cmd/fix/url_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -func init() { - addTestCases(urlTests, url) -} - -var urlTests = []testCase{ - { - Name: "url.0", - In: `package main - -import ( - "http" -) - -func f() { - var _ http.URL - http.ParseURL(a) - http.ParseURLReference(a) - http.ParseQuery(a) - m := http.Values{a: b} - http.URLEscape(a) - http.URLUnescape(a) - var x http.URLError - var y http.URLEscapeError -} -`, - Out: `package main - -import "url" - -func f() { - var _ url.URL - url.Parse(a) - url.ParseWithReference(a) - url.ParseQuery(a) - m := url.Values{a: b} - url.QueryEscape(a) - url.QueryUnescape(a) - var x url.Error - var y url.EscapeError -} -`, - }, - { - Name: "url.1", - In: `package main - -import ( - "http" -) - -func f() { - http.ParseURL(a) - var x http.Request -} -`, - Out: `package main - -import ( - "http" - "url" -) - -func f() { - url.Parse(a) - var x http.Request -} -`, - }, - { - Name: "url.2", - In: `package main - -import ( - "http" -) - -type U struct{ url int } -type M map[int]int - -func f() { - http.ParseURL(a) - var url = 23 - url, x := 45, y - _ = U{url: url} - _ = M{url + 1: url} -} - -func g(url string) string { - return url -} - -func h() (url string) { - return url -} -`, - Out: `package main - -import "url" - -type U struct{ url_ int } -type M map[int]int - -func f() { - url.Parse(a) - var url_ = 23 - url_, x := 45, y - _ = U{url_: url_} - _ = M{url_ + 1: url_} -} - -func g(url_ string) string { - return url_ -} - -func h() (url_ string) { - return url_ -} -`, - }, - { - Name: "url.3", - In: `package main - -import "http" - -type U struct{ url string } - -func f() { - var u U - u.url = "x" -} - -func (url *T) m() string { - return url -} -`, - Out: `package main - -import "http" - -type U struct{ url string } - -func f() { - var u U - u.url = "x" -} - -func (url *T) m() string { - return url -} -`, - }, -} diff --git a/src/cmd/fix/xmlapi.go b/src/cmd/fix/xmlapi.go deleted file mode 100644 index e74425914..000000000 --- a/src/cmd/fix/xmlapi.go +++ /dev/null @@ -1,111 +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 main - -import ( - "go/ast" -) - -func init() { - register(xmlapiFix) -} - -var xmlapiFix = fix{ - "xmlapi", - "2012-01-23", - xmlapi, - ` - Make encoding/xml's API look more like the rest of the encoding packages. - -http://codereview.appspot.com/5574053 -`, -} - -var xmlapiTypeConfig = &TypeConfig{ - Func: map[string]string{ - "xml.NewParser": "*xml.Parser", - "os.Open": "*os.File", - "os.OpenFile": "*os.File", - "bytes.NewBuffer": "*bytes.Buffer", - "bytes.NewBufferString": "*bytes.Buffer", - "bufio.NewReader": "*bufio.Reader", - "bufio.NewReadWriter": "*bufio.ReadWriter", - }, -} - -var isReader = map[string]bool{ - "*os.File": true, - "*bytes.Buffer": true, - "*bufio.Reader": true, - "*bufio.ReadWriter": true, - "io.Reader": true, -} - -func xmlapi(f *ast.File) bool { - if !imports(f, "encoding/xml") { - return false - } - - typeof, _ := typecheck(xmlapiTypeConfig, f) - - fixed := false - walk(f, func(n interface{}) { - s, ok := n.(*ast.SelectorExpr) - if ok && typeof[s.X] == "*xml.Parser" && s.Sel.Name == "Unmarshal" { - s.Sel.Name = "DecodeElement" - fixed = true - return - } - if ok && isPkgDot(s, "xml", "Parser") { - s.Sel.Name = "Decoder" - fixed = true - return - } - - call, ok := n.(*ast.CallExpr) - if !ok { - return - } - switch { - case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Marshal"): - *call = xmlMarshal(call.Args) - fixed = true - case len(call.Args) == 2 && isPkgDot(call.Fun, "xml", "Unmarshal"): - if isReader[typeof[call.Args[0]]] { - *call = xmlUnmarshal(call.Args) - fixed = true - } - case len(call.Args) == 1 && isPkgDot(call.Fun, "xml", "NewParser"): - sel := call.Fun.(*ast.SelectorExpr).Sel - sel.Name = "NewDecoder" - fixed = true - } - }) - return fixed -} - -func xmlMarshal(args []ast.Expr) ast.CallExpr { - return xmlCallChain("NewEncoder", "Encode", args) -} - -func xmlUnmarshal(args []ast.Expr) ast.CallExpr { - return xmlCallChain("NewDecoder", "Decode", args) -} - -func xmlCallChain(first, second string, args []ast.Expr) ast.CallExpr { - return ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("xml"), - Sel: ast.NewIdent(first), - }, - Args: args[:1], - }, - Sel: ast.NewIdent(second), - }, - Args: args[1:2], - } -} diff --git a/src/cmd/fix/xmlapi_test.go b/src/cmd/fix/xmlapi_test.go deleted file mode 100644 index 6486c8124..000000000 --- a/src/cmd/fix/xmlapi_test.go +++ /dev/null @@ -1,85 +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 main - -func init() { - addTestCases(xmlapiTests, xmlapi) -} - -var xmlapiTests = []testCase{ - { - Name: "xmlapi.0", - In: `package main - -import "encoding/xml" - -func f() { - xml.Marshal(a, b) - xml.Unmarshal(a, b) - - var buf1 bytes.Buffer - buf2 := &bytes.Buffer{} - buf3 := bytes.NewBuffer(data) - buf4 := bytes.NewBufferString(data) - buf5 := bufio.NewReader(r) - xml.Unmarshal(&buf1, v) - xml.Unmarshal(buf2, v) - xml.Unmarshal(buf3, v) - xml.Unmarshal(buf4, v) - xml.Unmarshal(buf5, v) - - f := os.Open("foo.xml") - xml.Unmarshal(f, v) - - p1 := xml.NewParser(stream) - p1.Unmarshal(v, start) - - var p2 *xml.Parser - p2.Unmarshal(v, start) -} - -func g(r io.Reader, f *os.File, b []byte) { - xml.Unmarshal(r, v) - xml.Unmarshal(f, v) - xml.Unmarshal(b, v) -} -`, - Out: `package main - -import "encoding/xml" - -func f() { - xml.NewEncoder(a).Encode(b) - xml.Unmarshal(a, b) - - var buf1 bytes.Buffer - buf2 := &bytes.Buffer{} - buf3 := bytes.NewBuffer(data) - buf4 := bytes.NewBufferString(data) - buf5 := bufio.NewReader(r) - xml.NewDecoder(&buf1).Decode(v) - xml.NewDecoder(buf2).Decode(v) - xml.NewDecoder(buf3).Decode(v) - xml.NewDecoder(buf4).Decode(v) - xml.NewDecoder(buf5).Decode(v) - - f := os.Open("foo.xml") - xml.NewDecoder(f).Decode(v) - - p1 := xml.NewDecoder(stream) - p1.DecodeElement(v, start) - - var p2 *xml.Decoder - p2.DecodeElement(v, start) -} - -func g(r io.Reader, f *os.File, b []byte) { - xml.NewDecoder(r).Decode(v) - xml.NewDecoder(f).Decode(v) - xml.Unmarshal(b, v) -} -`, - }, -} diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 6982bbe56..be9f552f6 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -15,8 +15,8 @@ static int defercalc; -uint32 -rnd(uint32 o, uint32 r) +vlong +rnd(vlong o, vlong r) { if(r < 1 || r > 8 || (r&(r-1)) != 0) fatal("rnd"); @@ -54,6 +54,11 @@ widstruct(Type *errtype, Type *t, vlong o, int flag) for(f=t->type; f!=T; f=f->down) { if(f->etype != TFIELD) fatal("widstruct: not TFIELD: %lT", f); + if(f->type == T) { + // broken field, just skip it so that other valid fields + // get a width. + continue; + } dowidth(f->type); if(f->type->align > maxalign) maxalign = f->type->align; @@ -248,8 +253,12 @@ dowidth(Type *t) checkwidth(t->type); t->align = widthptr; } - else if(t->bound == -100) - yyerror("use of [...] array outside of array literal"); + else if(t->bound == -100) { + if(!t->broke) { + yyerror("use of [...] array outside of array literal"); + t->broke = 1; + } + } else fatal("dowidth %T", t); // probably [...]T break; @@ -607,7 +616,7 @@ typeinit(void) fatal("typeinit: %s already defined", s->name); t = typ(etype); - t->sym = s; + t->sym = s1; dowidth(t); types[etype] = t; @@ -615,12 +624,12 @@ typeinit(void) } Array_array = rnd(0, widthptr); - Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width); - Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width); - sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr); + Array_nel = rnd(Array_array+widthptr, widthint); + Array_cap = rnd(Array_nel+widthint, widthint); + sizeof_Array = rnd(Array_cap+widthint, widthptr); // string is same as slice wo the cap - sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr); + sizeof_String = rnd(Array_nel+widthint, widthptr); dowidth(types[TSTRING]); dowidth(idealstring); diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors index 0f865d086..8886a8e52 100755 --- a/src/cmd/gc/bisonerrors +++ b/src/cmd/gc/bisonerrors @@ -41,9 +41,9 @@ grammar && NF>0 { } # In state dumps, record shift/reduce actions. -bison && /^state 0/ { grammar = 0; states = 1 } +bison && /^[Ss]tate 0/ { grammar = 0; states = 1 } -states && /^state / { state = $2 } +states && /^[Ss]tate / { state = $2 } states { statetext[state] = statetext[state] $0 "\n" } states && / shift/ { diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index ca3d6670d..7de448d1e 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -1,118 +1,119 @@ char *runtimeimport = "package runtime\n" "import runtime \"runtime\"\n" - "func @\"\".new(@\"\".typ *byte) (? *any)\n" - "func @\"\".panicindex()\n" - "func @\"\".panicslice()\n" - "func @\"\".throwreturn()\n" - "func @\"\".throwinit()\n" - "func @\"\".panicwrap(? string, ? string, ? string)\n" - "func @\"\".panic(? interface {})\n" - "func @\"\".recover(? *int32) (? interface {})\n" - "func @\"\".printbool(? bool)\n" - "func @\"\".printfloat(? float64)\n" - "func @\"\".printint(? int64)\n" - "func @\"\".printuint(? uint64)\n" - "func @\"\".printcomplex(? complex128)\n" - "func @\"\".printstring(? string)\n" - "func @\"\".printpointer(? any)\n" - "func @\"\".printiface(? any)\n" - "func @\"\".printeface(? any)\n" - "func @\"\".printslice(? any)\n" - "func @\"\".printnl()\n" - "func @\"\".printsp()\n" - "func @\"\".goprintf()\n" - "func @\"\".concatstring()\n" - "func @\"\".append()\n" - "func @\"\".appendslice(@\"\".typ *byte, @\"\".x any, @\"\".y []any) (? any)\n" - "func @\"\".appendstr(@\"\".typ *byte, @\"\".x []byte, @\"\".y string) (? []byte)\n" - "func @\"\".cmpstring(? string, ? string) (? int)\n" - "func @\"\".slicestring(? string, ? int, ? int) (? string)\n" - "func @\"\".slicestring1(? string, ? int) (? string)\n" - "func @\"\".intstring(? int64) (? string)\n" - "func @\"\".slicebytetostring(? []byte) (? string)\n" - "func @\"\".slicerunetostring(? []rune) (? string)\n" - "func @\"\".stringtoslicebyte(? string) (? []byte)\n" - "func @\"\".stringtoslicerune(? string) (? []rune)\n" - "func @\"\".stringiter(? string, ? int) (? int)\n" - "func @\"\".stringiter2(? string, ? int) (@\"\".retk int, @\"\".retv rune)\n" - "func @\"\".copy(@\"\".to any, @\"\".fr any, @\"\".wid uint32) (? int)\n" - "func @\"\".slicestringcopy(@\"\".to any, @\"\".fr any) (? int)\n" - "func @\"\".convI2E(@\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convI2I(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convT2E(@\"\".typ *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".convT2I(@\"\".typ *byte, @\"\".typ2 *byte, @\"\".elem any) (@\"\".ret any)\n" - "func @\"\".assertE2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertE2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertE2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertE2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2E(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2E2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2I(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2I2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".assertI2T(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any)\n" - "func @\"\".assertI2T2(@\"\".typ *byte, @\"\".iface any) (@\"\".ret any, @\"\".ok bool)\n" - "func @\"\".ifaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n" - "func @\"\".efaceeq(@\"\".i1 any, @\"\".i2 any) (@\"\".ret bool)\n" - "func @\"\".ifacethash(@\"\".i1 any) (@\"\".ret uint32)\n" - "func @\"\".efacethash(@\"\".i1 any) (@\"\".ret uint32)\n" - "func @\"\".equal(@\"\".typ *byte, @\"\".x1 any, @\"\".x2 any) (@\"\".ret bool)\n" - "func @\"\".makemap(@\"\".mapType *byte, @\"\".hint int64) (@\"\".hmap map[any]any)\n" - "func @\"\".mapaccess1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any)\n" - "func @\"\".mapaccess2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any) (@\"\".val any, @\"\".pres bool)\n" - "func @\"\".mapassign1(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any)\n" - "func @\"\".mapassign2(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any, @\"\".val any, @\"\".pres bool)\n" - "func @\"\".mapiterinit(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".hiter *any)\n" - "func @\"\".mapdelete(@\"\".mapType *byte, @\"\".hmap map[any]any, @\"\".key any)\n" - "func @\"\".mapiternext(@\"\".hiter *any)\n" - "func @\"\".mapiter1(@\"\".hiter *any) (@\"\".key any)\n" - "func @\"\".mapiter2(@\"\".hiter *any) (@\"\".key any, @\"\".val any)\n" - "func @\"\".makechan(@\"\".chanType *byte, @\"\".hint int64) (@\"\".hchan chan any)\n" - "func @\"\".chanrecv1(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any)\n" - "func @\"\".chanrecv2(@\"\".chanType *byte, @\"\".hchan <-chan any) (@\"\".elem any, @\"\".received bool)\n" - "func @\"\".chansend1(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any)\n" - "func @\"\".closechan(@\"\".hchan any)\n" - "func @\"\".selectnbsend(@\"\".chanType *byte, @\"\".hchan chan<- any, @\"\".elem any) (? bool)\n" - "func @\"\".selectnbrecv(@\"\".chanType *byte, @\"\".elem *any, @\"\".hchan <-chan any) (? bool)\n" - "func @\"\".selectnbrecv2(@\"\".chanType *byte, @\"\".elem *any, @\"\".received *bool, @\"\".hchan <-chan any) (? bool)\n" - "func @\"\".newselect(@\"\".size int) (@\"\".sel *byte)\n" - "func @\"\".selectsend(@\"\".sel *byte, @\"\".hchan chan<- any, @\"\".elem *any) (@\"\".selected bool)\n" - "func @\"\".selectrecv(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any) (@\"\".selected bool)\n" - "func @\"\".selectrecv2(@\"\".sel *byte, @\"\".hchan <-chan any, @\"\".elem *any, @\"\".received *bool) (@\"\".selected bool)\n" - "func @\"\".selectdefault(@\"\".sel *byte) (@\"\".selected bool)\n" - "func @\"\".selectgo(@\"\".sel *byte)\n" - "func @\"\".block()\n" - "func @\"\".makeslice(@\"\".typ *byte, @\"\".nel int64, @\"\".cap int64) (@\"\".ary []any)\n" - "func @\"\".growslice(@\"\".typ *byte, @\"\".old []any, @\"\".n int64) (@\"\".ary []any)\n" - "func @\"\".sliceslice1(@\"\".old []any, @\"\".lb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".sliceslice(@\"\".old []any, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".slicearray(@\"\".old *any, @\"\".nel uint64, @\"\".lb uint64, @\"\".hb uint64, @\"\".width uint64) (@\"\".ary []any)\n" - "func @\"\".closure()\n" - "func @\"\".memequal(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal8(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal16(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal32(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal64(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".memequal128(@\"\".eq *bool, @\"\".size uintptr, @\"\".x *any, @\"\".y *any)\n" - "func @\"\".int64div(? int64, ? int64) (? int64)\n" - "func @\"\".uint64div(? uint64, ? uint64) (? uint64)\n" - "func @\"\".int64mod(? int64, ? int64) (? int64)\n" - "func @\"\".uint64mod(? uint64, ? uint64) (? uint64)\n" - "func @\"\".float64toint64(? float64) (? int64)\n" - "func @\"\".float64touint64(? float64) (? uint64)\n" - "func @\"\".int64tofloat64(? int64) (? float64)\n" - "func @\"\".uint64tofloat64(? uint64) (? float64)\n" - "func @\"\".complex128div(@\"\".num complex128, @\"\".den complex128) (@\"\".quo complex128)\n" + "func @\"\".new (@\"\".typ·2 *byte) (? *any)\n" + "func @\"\".panicindex ()\n" + "func @\"\".panicslice ()\n" + "func @\"\".throwreturn ()\n" + "func @\"\".throwinit ()\n" + "func @\"\".panicwrap (? string, ? string, ? string)\n" + "func @\"\".panic (? interface {})\n" + "func @\"\".recover (? *int32) (? interface {})\n" + "func @\"\".printbool (? bool)\n" + "func @\"\".printfloat (? float64)\n" + "func @\"\".printint (? int64)\n" + "func @\"\".printuint (? uint64)\n" + "func @\"\".printcomplex (? complex128)\n" + "func @\"\".printstring (? string)\n" + "func @\"\".printpointer (? any)\n" + "func @\"\".printiface (? any)\n" + "func @\"\".printeface (? any)\n" + "func @\"\".printslice (? any)\n" + "func @\"\".printnl ()\n" + "func @\"\".printsp ()\n" + "func @\"\".goprintf ()\n" + "func @\"\".concatstring ()\n" + "func @\"\".append ()\n" + "func @\"\".appendslice (@\"\".typ·2 *byte, @\"\".x·3 any, @\"\".y·4 []any) (? any)\n" + "func @\"\".appendstr (@\"\".typ·2 *byte, @\"\".x·3 []byte, @\"\".y·4 string) (? []byte)\n" + "func @\"\".cmpstring (? string, ? string) (? int)\n" + "func @\"\".eqstring (? string, ? string) (? bool)\n" + "func @\"\".intstring (? int64) (? string)\n" + "func @\"\".slicebytetostring (? []byte) (? string)\n" + "func @\"\".slicerunetostring (? []rune) (? string)\n" + "func @\"\".stringtoslicebyte (? string) (? []byte)\n" + "func @\"\".stringtoslicerune (? string) (? []rune)\n" + "func @\"\".stringiter (? string, ? int) (? int)\n" + "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" + "func @\"\".copy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" + "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n" + "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n" + "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n" + "func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + "func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n" + "func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertE2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertE2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertE2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n" + "func @\"\".assertI2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n" + "func @\"\".assertI2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n" + "func @\"\".assertE2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n" + "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" + "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" + "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n" + "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n" + "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 any)\n" + "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 any, @\"\".pres·2 bool)\n" + "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any, @\"\".val·4 any)\n" + "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n" + "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 any)\n" + "func @\"\".mapiternext (@\"\".hiter·1 *any)\n" + "func @\"\".mapiter1 (@\"\".hiter·2 *any) (@\"\".key·1 any)\n" + "func @\"\".mapiter2 (@\"\".hiter·3 *any) (@\"\".key·1 any, @\"\".val·2 any)\n" + "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" + "func @\"\".chanrecv1 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any) (@\"\".elem·1 any)\n" + "func @\"\".chanrecv2 (@\"\".chanType·3 *byte, @\"\".hchan·4 <-chan any) (@\"\".elem·1 any, @\"\".received·2 bool)\n" + "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 any)\n" + "func @\"\".closechan (@\"\".hchan·1 any)\n" + "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 any) (? bool)\n" + "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n" + "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n" + "func @\"\".newselect (@\"\".size·2 int32) (@\"\".sel·1 *byte)\n" + "func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + "func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n" + "func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n" + "func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n" + "func @\"\".selectgo (@\"\".sel·1 *byte)\n" + "func @\"\".block ()\n" + "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n" + "func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal32 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal64 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal128 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".int64div (? int64, ? int64) (? int64)\n" + "func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n" + "func @\"\".int64mod (? int64, ? int64) (? int64)\n" + "func @\"\".uint64mod (? uint64, ? uint64) (? uint64)\n" + "func @\"\".float64toint64 (? float64) (? int64)\n" + "func @\"\".float64touint64 (? float64) (? uint64)\n" + "func @\"\".int64tofloat64 (? int64) (? float64)\n" + "func @\"\".uint64tofloat64 (? uint64) (? float64)\n" + "func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n" + "func @\"\".racefuncenter (? uintptr)\n" + "func @\"\".racefuncexit ()\n" + "func @\"\".raceread (? uintptr)\n" + "func @\"\".racewrite (? uintptr)\n" "\n" "$$\n"; char *unsafeimport = "package unsafe\n" "import runtime \"runtime\"\n" "type @\"\".Pointer uintptr\n" - "func @\"\".Offsetof(? any) (? uintptr)\n" - "func @\"\".Sizeof(? any) (? uintptr)\n" - "func @\"\".Alignof(? any) (? uintptr)\n" + "func @\"\".Offsetof (? any) (? uintptr)\n" + "func @\"\".Sizeof (? any) (? uintptr)\n" + "func @\"\".Alignof (? any) (? uintptr)\n" "\n" "$$\n"; diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index fa44e40fa..4e029ef83 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -75,6 +75,8 @@ closurebody(NodeList *body) return func; } +static Node* makeclosure(Node *func, int nowrap); + void typecheckclosure(Node *func, int top) { @@ -85,12 +87,12 @@ typecheckclosure(Node *func, int top) oldfn = curfn; typecheck(&func->ntype, Etype); func->type = func->ntype->type; - if(curfn == nil) { - xtop = list(xtop, func); - return; - } - - if(func->type != T) { + + // 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 xtop. + if(curfn && func->type != T) { curfn = func; typechecklist(func->nbody, Etop); curfn = oldfn; @@ -120,26 +122,43 @@ typecheckclosure(Node *func, int top) func->enter = list(func->enter, v->heapaddr); v->heapaddr = N; } + + // Create top-level function + xtop = list(xtop, makeclosure(func, func->cvars==nil || (top&Ecall))); } static Node* -makeclosure(Node *func, NodeList **init, int nowrap) +makeclosure(Node *func, int nowrap) { - Node *xtype, *v, *addr, *xfunc; - NodeList *l; + Node *xtype, *v, *addr, *xfunc, *cv; + NodeList *l, *body; static int closgen; char *p; - - USED(init); + int offset; /* * wrap body in external function - * with extra closure parameters. + * that begins by reading closure parameters. */ xtype = nod(OTFUNC, N, N); + xtype->list = func->list; + xtype->rlist = func->rlist; - // each closure variable has a corresponding - // address parameter. + // create the function + xfunc = nod(ODCLFUNC, N, N); + snprint(namebuf, sizeof namebuf, "func·%.3d", ++closgen); + xfunc->nname = newname(lookup(namebuf)); + xfunc->nname->sym->flags |= SymExported; // disable export + xfunc->nname->ntype = xtype; + xfunc->nname->defn = xfunc; + declare(xfunc->nname, PFUNC); + xfunc->nname->funcdepth = func->funcdepth; + xfunc->funcdepth = func->funcdepth; + + // declare variables holding addresses taken from closure + // and initialize in entry prologue. + body = nil; + offset = widthptr; for(l=func->cvars; l; l=l->next) { v = l->n; if(v->op == 0) @@ -149,38 +168,35 @@ makeclosure(Node *func, NodeList **init, int nowrap) addr->sym = lookup(p); free(p); addr->ntype = nod(OIND, typenod(v->type), N); - addr->class = PPARAM; + addr->class = PAUTO; addr->addable = 1; addr->ullman = 1; - + addr->used = 1; + addr->curfn = xfunc; + xfunc->dcl = list(xfunc->dcl, addr); v->heapaddr = addr; - - xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype)); + cv = nod(OCLOSUREVAR, N, N); + cv->type = ptrto(v->type); + cv->xoffset = offset; + body = list(body, nod(OAS, addr, cv)); + offset += widthptr; } + typechecklist(body, Etop); + walkstmtlist(body); + xfunc->enter = body; - // then a dummy arg where the closure's caller pc sits - if (!nowrap) - xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); - - // then the function arguments - xtype->list = concat(xtype->list, func->list); - xtype->rlist = concat(xtype->rlist, func->rlist); - - // create the function - xfunc = nod(ODCLFUNC, N, N); - snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen); - xfunc->nname = newname(lookup(namebuf)); - xfunc->nname->ntype = xtype; - xfunc->nname->defn = xfunc; - declare(xfunc->nname, PFUNC); - xfunc->nname->funcdepth = func->funcdepth; - xfunc->funcdepth = func->funcdepth; xfunc->nbody = func->nbody; - xfunc->dcl = func->dcl; + xfunc->dcl = concat(func->dcl, xfunc->dcl); if(xfunc->nbody == nil) fatal("empty body - won't generate any code"); typecheck(&xfunc, Etop); - closures = list(closures, xfunc); + + xfunc->closure = func; + func->closure = xfunc; + + func->nbody = nil; + func->list = nil; + func->rlist = nil; return xfunc; } @@ -188,51 +204,55 @@ makeclosure(Node *func, NodeList **init, int nowrap) Node* walkclosure(Node *func, NodeList **init) { + Node *clos, *typ; + NodeList *l; + char buf[20]; int narg; - Node *xtype, *xfunc, *call, *clos; - NodeList *l, *in; - // no closure vars, don't bother wrapping + // If no closure vars, don't bother wrapping. if(func->cvars == nil) - return makeclosure(func, init, 1)->nname; + return func->closure->nname; + + // Create closure in the form of a composite literal. + // supposing the closure captures an int i and a string s + // and has one float64 argument and no results, + // the generated code looks like: + // + // clos = &struct{F uintptr; A0 *int; A1 *string}{func·001, &i, &s} + // + // The use of the struct provides type information to the garbage + // collector so that it can walk the closure. We could use (in this case) + // [3]unsafe.Pointer instead, but that would leave the gc in the dark. + // 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. - /* - * wrap body in external function - * with extra closure parameters. - */ - - // create the function - xfunc = makeclosure(func, init, 0); - xtype = xfunc->nname->ntype; - - // prepare call of sys.closure that turns external func into func literal value. - clos = syslook("closure", 1); - clos->type = T; - clos->ntype = nod(OTFUNC, N, N); - in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz - in = list(in, nod(ODCLFIELD, N, xtype)); narg = 0; + typ = nod(OTSTRUCT, N, N); + typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR]))); for(l=func->cvars; l; l=l->next) { if(l->n->op == 0) continue; - narg++; - in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype)); + snprint(buf, sizeof buf, "A%d", narg++); + typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype)); } - clos->ntype->list = in; - clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type))); + + clos = nod(OCOMPLIT, N, nod(OIND, typ, N)); + clos->esc = func->esc; + clos->right->implicit = 1; + clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter); + + // Force type conversion from *struct to the func type. + clos = nod(OCONVNOP, clos, N); + clos->type = func->type; + typecheck(&clos, Erv); + // typecheck will insert a PTRLIT node under CONVNOP, + // tag it with escape analysis result. + clos->left->esc = func->esc; + walkexpr(&clos, init); - call = nod(OCALL, clos, N); - if(narg*widthptr > 100) - yyerror("closure needs too many variables; runtime will reject it"); - in = list1(nodintconst(narg*widthptr)); - in = list(in, xfunc->nname); - in = concat(in, func->enter); - call->list = in; - - typecheck(&call, Erv); - walkexpr(&call, init); - return call; + return clos; } // Special case for closures that get called in place. @@ -242,6 +262,7 @@ walkclosure(Node *func, NodeList **init) void walkcallclosure(Node *n, NodeList **init) { + USED(init); if (n->op != OCALLFUNC || n->left->op != OCLOSURE) { dump("walkcallclosure", n); fatal("abuse of walkcallclosure"); @@ -250,7 +271,7 @@ walkcallclosure(Node *n, NodeList **init) // New arg list for n. First the closure-args // and then the original parameter list. n->list = concat(n->left->enter, n->list); - n->left = makeclosure(n->left, init, 1)->nname; + n->left = n->left->closure->nname; dowidth(n->left->type); n->type = getoutargx(n->left->type); // for a single valued function, pull the field type out of the struct diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index e27c88338..4f1ff6778 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -87,8 +87,12 @@ convlit1(Node **np, Type *t, int explicit) switch(n->op) { default: - if(n->type == idealbool) - n->type = types[TBOOL]; + if(n->type == idealbool) { + if(t->etype == TBOOL) + n->type = t; + else + n->type = types[TBOOL]; + } if(n->type->etype == TIDEAL) { convlit(&n->left, t); convlit(&n->right, t); @@ -162,6 +166,16 @@ convlit1(Node **np, Type *t, int explicit) case TFUNC: case TUNSAFEPTR: break; + + case TUINTPTR: + // A nil literal may be converted to uintptr + // if it is an unsafe.Pointer + if(n->type->etype == TUNSAFEPTR) { + n->val.u.xval = mal(sizeof(*n->val.u.xval)); + mpmovecfix(n->val.u.xval, 0); + n->val.ctype = CTINT; + } else + goto bad; } break; @@ -230,7 +244,8 @@ convlit1(Node **np, Type *t, int explicit) bad: if(!n->diag) { - yyerror("cannot convert %N to type %T", n, t); + if(!t->broke) + yyerror("cannot convert %N to type %T", n, t); n->diag = 1; } if(isideal(n->type)) { @@ -348,13 +363,9 @@ toint(Val v) return v; } -void -overflow(Val v, Type *t) +int +doesoverflow(Val v, Type *t) { - // v has already been converted - // to appropriate form for t. - if(t == T || t->etype == TIDEAL) - return; switch(v.ctype) { case CTINT: case CTRUNE: @@ -362,14 +373,14 @@ overflow(Val v, Type *t) fatal("overflow: %T integer constant", t); if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 || mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0) - yyerror("constant %B overflows %T", v.u.xval, t); + return 1; break; case CTFLT: if(!isfloat[t->etype]) fatal("overflow: %T floating-point constant", t); if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 || mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0) - yyerror("constant %#F overflows %T", v.u.fval, t); + return 1; break; case CTCPLX: if(!iscomplex[t->etype]) @@ -378,7 +389,33 @@ overflow(Val v, Type *t) mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 || mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 || mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0) - yyerror("constant %#F overflows %T", v.u.fval, t); + return 1; + break; + } + return 0; +} + +void +overflow(Val v, Type *t) +{ + // v has already been converted + // to appropriate form for t. + if(t == T || t->etype == TIDEAL) + return; + + if(!doesoverflow(v, t)) + return; + + switch(v.ctype) { + case CTINT: + case CTRUNE: + yyerror("constant %B overflows %T", v.u.xval, t); + break; + case CTFLT: + yyerror("constant %#F overflows %T", v.u.fval, t); + break; + case CTCPLX: + yyerror("constant %#F overflows %T", v.u.fval, t); break; } } @@ -437,6 +474,20 @@ isconst(Node *n, int ct) return t == ct || (ct == CTINT && t == CTRUNE); } +static Node* +saveorig(Node *n) +{ + Node *n1; + + if(n == n->orig) { + // duplicate node for n->orig. + n1 = nod(OLITERAL, N, N); + n->orig = n1; + *n1 = *n; + } + return n->orig; +} + /* * if n is constant, rewrite as OLITERAL node. */ @@ -902,12 +953,7 @@ unary: } ret: - if(n == n->orig) { - // duplicate node for n->orig. - norig = nod(OLITERAL, N, N); - *norig = *n; - } else - norig = n->orig; + norig = saveorig(n); *n = *nl; // restore value of n->orig. n->orig = norig; @@ -924,11 +970,15 @@ ret: return; settrue: + norig = saveorig(n); *n = *nodbool(1); + n->orig = norig; return; setfalse: + norig = saveorig(n); *n = *nodbool(0); + n->orig = norig; return; } @@ -984,79 +1034,88 @@ nodcplxlit(Val r, Val i) return n; } -// TODO(rsc): combine with convlit +// idealkind returns a constant kind like consttype +// but for an arbitrary "ideal" expression. +static int +idealkind(Node *n) +{ + int k1, k2; + + if(n == N || !isideal(n->type)) + return CTxxx; + + switch(n->op) { + default: + return CTxxx; + case OLITERAL: + return n->val.ctype; + case OADD: + case OAND: + case OANDNOT: + case OCOM: + case ODIV: + case OMINUS: + case OMOD: + case OMUL: + case OSUB: + case OXOR: + case OOR: + case OPLUS: + // numeric kinds. + k1 = idealkind(n->left); + k2 = idealkind(n->right); + if(k1 > k2) + return k1; + else + return k2; + case OADDSTR: + return CTSTR; + case OANDAND: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLT: + case ONE: + case ONOT: + case OOROR: + case OCMPSTR: + case OCMPIFACE: + return CTBOOL; + case OLSH: + case ORSH: + // shifts (beware!). + return idealkind(n->left); + } +} + void defaultlit(Node **np, Type *t) { int lno; + int ctype; Node *n, *nn; + Type *t1; n = *np; if(n == N || !isideal(n->type)) return; - switch(n->op) { - case OLITERAL: + if(n->op == OLITERAL) { nn = nod(OXXX, N, N); *nn = *n; n = nn; *np = n; - break; - case OLSH: - case ORSH: - defaultlit(&n->left, t); - t = n->left->type; - if(t != T && !isint[t->etype]) { - yyerror("invalid operation: %N (shift of type %T)", n, t); - t = T; - } - n->type = t; - return; - case ONOT: - defaultlit(&n->left, t); - n->type = n->left->type; - return; - default: - if(n->left == N) { - dump("defaultlit", n); - fatal("defaultlit"); - } - // n is ideal, so left and right must both be ideal. - // n has not been computed as a constant value, - // so either left or right must not be constant. - // The only 'ideal' non-constant expressions are shifts. Ugh. - // If one of these is a shift and the other is not, use that type. - // When compiling x := 1<<i + 3.14, this means we try to push - // the float64 down into the 1<<i, producing the correct error - // (cannot shift float64). - if(t == T && (n->right->op == OLSH || n->right->op == ORSH)) { - defaultlit(&n->left, T); - defaultlit(&n->right, n->left->type); - } else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) { - defaultlit(&n->right, T); - defaultlit(&n->left, n->right->type); - } else if(iscmp[n->op]) { - defaultlit2(&n->left, &n->right, 1); - } else { - defaultlit(&n->left, t); - defaultlit(&n->right, t); - } - if(n->type == idealbool || n->type == idealstring) { - if(t != T && t->etype == n->type->etype) - n->type = t; - else - n->type = types[n->type->etype]; - } else - n->type = n->left->type; - return; } lno = setlineno(n); - switch(n->val.ctype) { + ctype = idealkind(n); + t1 = T; + switch(ctype) { default: if(t != T) { convlit(np, t); - break; + return; } if(n->val.ctype == CTNIL) { lineno = lno; @@ -1065,46 +1124,52 @@ defaultlit(Node **np, Type *t) break; } if(n->val.ctype == CTSTR) { - n->type = types[TSTRING]; + t1 = types[TSTRING]; + convlit(np, t1); break; } yyerror("defaultlit: unknown literal: %N", n); break; + case CTxxx: + fatal("defaultlit: idealkind is CTxxx: %+N", n); + break; case CTBOOL: - n->type = types[TBOOL]; + t1 = types[TBOOL]; if(t != T && t->etype == TBOOL) - n->type = t; + t1 = t; + convlit(np, t1); break; case CTINT: - n->type = types[TINT]; + t1 = types[TINT]; goto num; case CTRUNE: - n->type = runetype; + t1 = runetype; goto num; case CTFLT: - n->type = types[TFLOAT64]; + t1 = types[TFLOAT64]; goto num; case CTCPLX: - n->type = types[TCOMPLEX128]; + t1 = types[TCOMPLEX128]; goto num; num: if(t != T) { if(isint[t->etype]) { - n->type = t; + t1 = t; n->val = toint(n->val); } else if(isfloat[t->etype]) { - n->type = t; + t1 = t; n->val = toflt(n->val); } else if(iscomplex[t->etype]) { - n->type = t; + t1 = t; n->val = tocplx(n->val); } } - overflow(n->val, n->type); + overflow(n->val, t1); + convlit(np, t1); break; } lineno = lno; @@ -1206,6 +1271,7 @@ smallintconst(Node *n) case TIDEAL: case TINT64: case TUINT64: + case TPTR64: if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0 || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) break; @@ -1392,3 +1458,132 @@ cmplxdiv(Mpcplx *v, Mpcplx *rv) mpsubfltflt(&v->imag, &ad); // bc-ad mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd) } + +static int hascallchan(Node*); + +// Is n a Go language constant (as opposed to a compile-time constant)? +// Expressions derived from nil, like string([]byte(nil)), while they +// may be known at compile time, are not Go language constants. +// Only called for expressions known to evaluated to compile-time +// constants. +int +isgoconst(Node *n) +{ + Node *l; + Type *t; + + if(n->orig != N) + n = n->orig; + + switch(n->op) { + case OADD: + case OADDSTR: + case OAND: + case OANDAND: + case OANDNOT: + case OCOM: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLSH: + case OLT: + case OMINUS: + case OMOD: + case OMUL: + case ONE: + case ONOT: + case OOR: + case OOROR: + case OPLUS: + case ORSH: + case OSUB: + case OXOR: + case OCONV: + case OIOTA: + case OCOMPLEX: + case OREAL: + case OIMAG: + if(isgoconst(n->left) && (n->right == N || isgoconst(n->right))) + return 1; + break; + + case OLEN: + case OCAP: + l = n->left; + if(isgoconst(l)) + return 1; + // Special case: len/cap is constant when applied to array or + // pointer to array when the expression does not contain + // function calls or channel receive operations. + t = l->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t) && !hascallchan(l)) + return 1; + break; + + case OLITERAL: + if(n->val.ctype != CTNIL) + return 1; + break; + + case ONAME: + l = n->sym->def; + if(l->op == OLITERAL && n->val.ctype != CTNIL) + return 1; + break; + + case ONONAME: + if(n->sym->def != N && n->sym->def->op == OIOTA) + return 1; + break; + + case OCALL: + // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. + l = n->left; + while(l->op == OPAREN) + l = l->left; + if(l->op != ONAME || l->sym->pkg != unsafepkg) + break; + if(strcmp(l->sym->name, "Alignof") == 0 || + strcmp(l->sym->name, "Offsetof") == 0 || + strcmp(l->sym->name, "Sizeof") == 0) + return 1; + break; + } + + //dump("nonconst", n); + return 0; +} + +static int +hascallchan(Node *n) +{ + NodeList *l; + + if(n == N) + return 0; + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + case ORECV: + return 1; + } + + if(hascallchan(n->left) || + hascallchan(n->right)) + return 1; + + for(l=n->list; l; l=l->next) + if(hascallchan(l->n)) + return 1; + for(l=n->rlist; l; l=l->next) + if(hascallchan(l->n)) + return 1; + + return 0; +} diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index dea7bc3bb..e0127fc59 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -271,7 +271,7 @@ complexgen(Node *n, Node *res) } void -complexbool(int op, Node *nl, Node *nr, int true, Prog *to) +complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to) { Node tnl, tnr; Node n1, n2, n3, n4; @@ -323,7 +323,7 @@ complexbool(int op, Node *nl, Node *nr, int true, Prog *to) if(op == ONE) true = !true; - bgen(&na, true, to); + bgen(&na, true, likely, to); } void diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 32f334b71..aa2489d9a 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -150,16 +150,35 @@ testdclstack(void) void redeclare(Sym *s, char *where) { - if(s->lastlineno == 0) - yyerror("%S redeclared %s\n" - "\tprevious declaration during import", - s, where); - else + Strlit *pkgstr; + int line1, line2; + + if(s->lastlineno == 0) { + pkgstr = s->origpkg ? s->origpkg->path : s->pkg->path; yyerror("%S redeclared %s\n" + "\tprevious declaration during import \"%Z\"", + s, where, pkgstr); + } else { + line1 = parserline(); + line2 = 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 == N) { + line2 = line1; + line1 = s->lastlineno; + } + + yyerrorl(line1, "%S redeclared %s\n" "\tprevious declaration at %L", - s, where, s->lastlineno); + s, where, line2); + } } +static int vargen; + /* * declare individual names - var, typ, const */ @@ -168,7 +187,10 @@ declare(Node *n, int ctxt) { Sym *s; int gen; - static int typegen, vargen; + static int typegen; + + if(ctxt == PDISCARD) + return; if(isblank(n)) return; @@ -180,6 +202,9 @@ declare(Node *n, int ctxt) if(importpkg == nil && !typecheckok && s->pkg != localpkg) yyerror("cannot declare name %S", s); + if(ctxt == PEXTERN && strcmp(s->name, "init") == 0) + yyerror("cannot declare init - must be func", s); + gen = 0; if(ctxt == PEXTERN) { externdcl = list(externdcl, n); @@ -192,7 +217,7 @@ declare(Node *n, int ctxt) curfn->dcl = list(curfn->dcl, n); if(n->op == OTYPE) gen = ++typegen; - else if(n->op == ONAME) + else if(n->op == ONAME && ctxt == PAUTO && strstr(s->name, "·") == nil) gen = ++vargen; pushdcl(s); n->curfn = curfn; @@ -237,7 +262,7 @@ variter(NodeList *vl, Node *t, NodeList *el) init = nil; doexpr = el != nil; - + if(count(el) == 1 && count(vl) > 1) { e = el->n; as2 = nod(OAS2, N, N); @@ -464,7 +489,7 @@ colasdefn(NodeList *left, Node *defn) if(isblank(n)) continue; if(!colasname(n)) { - yyerror("non-name %N on left side of :=", n); + yyerrorl(defn->lineno, "non-name %N on left side of :=", n); nerr++; continue; } @@ -479,11 +504,11 @@ colasdefn(NodeList *left, Node *defn) l->n = n; } if(nnew == 0 && nerr == 0) - yyerror("no new variables on left side of :="); + yyerrorl(defn->lineno, "no new variables on left side of :="); } Node* -colas(NodeList *left, NodeList *right) +colas(NodeList *left, NodeList *right, int32 lno) { Node *as; @@ -491,6 +516,7 @@ colas(NodeList *left, NodeList *right) as->list = left; as->rlist = right; as->colas = 1; + as->lineno = lno; colasdefn(left, as); // make the tree prettier; not necessary @@ -515,7 +541,7 @@ ifacedcl(Node *n) if(n->op != ODCLFIELD || n->right == N) fatal("ifacedcl"); - dclcontext = PAUTO; + dclcontext = PPARAM; markdcl(); funcdepth++; n->outer = curfn; @@ -526,6 +552,7 @@ ifacedcl(Node *n) // seen the body of a function but since an interface // field declaration does not have a body, we must // call it now to pop the current declaration context. + dclcontext = PAUTO; funcbody(n); } @@ -567,6 +594,11 @@ funcargs(Node *nt) if(nt->op != OTFUNC) fatal("funcargs %O", nt->op); + // re-start the variable generation number + // we want to use small numbers for the return variables, + // so let them have the chunk starting at 1. + vargen = count(nt->rlist); + // declare the receiver and in arguments. // no n->defn because type checking of func header // will not fill in the types until later @@ -578,6 +610,8 @@ funcargs(Node *nt) n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); + if(dclcontext == PAUTO) + n->left->vargen = ++vargen; } } for(l=nt->list; l; l=l->next) { @@ -588,29 +622,44 @@ funcargs(Node *nt) n->left->op = ONAME; n->left->ntype = n->right; declare(n->left, PPARAM); + if(dclcontext == PAUTO) + n->left->vargen = ++vargen; } } // declare the out arguments. - gen = 0; + gen = count(nt->list); + int i = 0; for(l=nt->rlist; l; l=l->next) { n = l->n; + if(n->op != ODCLFIELD) fatal("funcargs out %O", n->op); - if(n->left != N) { - n->left->op = ONAME; - n->left->ntype = n->right; - if(isblank(n->left)) { - // Give it a name so we can assign to it during return. - // preserve the original in ->orig - nn = nod(OXXX, N, N); - *nn = *n->left; - n->left = nn; - snprint(namebuf, sizeof(namebuf), ".anon%d", gen++); - n->left->sym = lookup(namebuf); - } - declare(n->left, PPARAMOUT); + + if(n->left == N) { + // give it a name so escape analysis has nodes to work with + snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); + n->left = newname(lookup(namebuf)); + // TODO: n->left->missing = 1; + } + + n->left->op = ONAME; + + if(isblank(n->left)) { + // Give it a name so we can assign to it during return. + // preserve the original in ->orig + nn = nod(OXXX, N, N); + *nn = *n->left; + n->left = nn; + + snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); + n->left->sym = lookup(namebuf); } + + n->left->ntype = n->right; + declare(n->left, PPARAMOUT); + if(dclcontext == PAUTO) + n->left->vargen = ++i; } } @@ -978,8 +1027,11 @@ embedded(Sym *s) *utfrune(name, CenterDot) = 0; } - if(exportname(name) || s->pkg == builtinpkg) // old behaviour, tests pass, but is it correct? + if(exportname(name)) n = newname(lookup(name)); + else if(s->pkg == builtinpkg && importpkg != nil) + // The name of embedded builtins during imports belongs to importpkg. + n = newname(pkglookup(name, importpkg)); else n = newname(pkglookup(name, s->pkg)); n = nod(ODCLFIELD, n, oldname(s)); @@ -1124,6 +1176,7 @@ functype(Node *this, NodeList *in, NodeList *out) { Type *t; NodeList *rcvr; + Sym *s; t = typ(TFUNC); @@ -1141,7 +1194,12 @@ functype(Node *this, NodeList *in, NodeList *out) t->thistuple = 1; t->outtuple = count(out); t->intuple = count(in); - t->outnamed = t->outtuple > 0 && out->n->left != N; + t->outnamed = 0; + if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) { + s = out->n->left->orig->sym; + if(s != S && s->name[0] != '~') + t->outnamed = 1; + } return t; } @@ -1249,7 +1307,7 @@ methodname1(Node *n, Node *t) * n is fieldname, pa is base type, t is function type */ void -addmethod(Sym *sf, Type *t, int local) +addmethod(Sym *sf, Type *t, int local, int nointerface) { Type *f, *d, *pa; Node *n; @@ -1332,6 +1390,7 @@ addmethod(Sym *sf, Type *t, int local) } f = structfield(n); + f->nointerface = nointerface; // during import unexported method names should be in the type's package if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg) @@ -1382,3 +1441,20 @@ funccompile(Node *n, int isclosure) funcdepth = 0; dclcontext = PEXTERN; } + +Sym* +funcsym(Sym *s) +{ + char *p; + Sym *s1; + + p = smprint("%s·f", s->name); + s1 = pkglookup(p, s->pkg); + free(p); + if(s1->def == N) { + s1->def = newname(s1); + s1->def->shortname = newname(s); + funcsyms = list(funcsyms, s1->def); + } + return s1; +} diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 8d8f8967b..791967708 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.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. +// +build ignore + /* Gc is the generic label for the family of Go compilers @@ -25,6 +27,8 @@ other packages. It is therefore not necessary when compiling client C of package P to read the files of P's dependencies, only the compiled output of P. +Command Line + Usage: go tool 6g [flags] file... The specified files must be Go source files and all part of the same package. @@ -48,15 +52,40 @@ Flags: disable optimizations -S write assembly language text to standard output (code only) - -SS + -S -S write assembly language text to standard output (code and data) -u disallow importing packages not marked as safe -V print the compiler version + -race + compile with race detection enabled There are also a number of debugging flags; run the command with no arguments to get a usage message. +Compiler Directives + +The compiler accepts two compiler directives in the form of // comments at the +beginning of a line. To distinguish them from non-directive comments, the directives +require no space between the slashes and the name of the directive. However, since +they are comments, tools unaware of the directive convention or of a particular +directive can skip over a directive like any other comment. + + //line path/to/file:linenumber + +The //line directive specifies that the source line that follows should be recorded +as having come from the given file path and line number. Successive lines are +recorded using increasing line numbers, until the next directive. This directive +typically appears in machine-generated code, so that compilers and debuggers +will show lines in the original input to the generator. + + //go:noescape + +The //go:noescape directive specifies that the next declaration in the file, which +must be a func without a body (meaning that it has an implementation not written +in Go) does not allow any of the pointers passed as arguments to escape into the +heap or into the values returned from the function. This information can be used as +during the compiler's escape analysis of Go code calling the function. */ -package documentation +package main diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 8a265ce59..46c06d10e 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -1,8 +1,162 @@ // 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. -// + // Escape analysis. + +#include <u.h> +#include <libc.h> +#include "go.h" + +// Run analysis on minimal sets of mutually recursive functions +// or single non-recursive functions, bottom up. +// +// Finding these sets is finding strongly connected components +// in the static call graph. The algorithm for doing that is taken +// from Sedgewick, Algorithms, Second Edition, p. 482, with two +// adaptations. +// +// First, a hidden closure function (n->curfn != N) cannot be the +// root of a connected component. Refusing to use it as a root +// forces it into the component of the function in which it appears. +// The analysis assumes that closures and the functions in which they +// appear are analyzed together, so that the aliasing between their +// variables can be modeled more precisely. +// +// Second, each function becomes two virtual nodes in the graph, +// with numbers n and n+1. We record the function's node number as n +// but search from node n+1. If the search tells us that the component +// number (min) is n+1, we know that this is a trivial component: one function +// plus its closures. If the search tells us that the component number is +// n, then there was a path from node n+1 back to node n, meaning that +// the function set is mutually recursive. The escape analysis can be +// more precise when analyzing a single non-recursive function than +// when analyzing a set of mutually recursive functions. + +static NodeList *stack; +static uint32 visitgen; +static uint32 visit(Node*); +static uint32 visitcode(Node*, uint32); +static uint32 visitcodelist(NodeList*, uint32); + +static void analyze(NodeList*, int); + +enum +{ + EscFuncUnknown = 0, + EscFuncPlanned, + EscFuncStarted, + EscFuncTagged, +}; + +void +escapes(NodeList *all) +{ + NodeList *l; + + for(l=all; l; l=l->next) + l->n->walkgen = 0; + + visitgen = 0; + for(l=all; l; l=l->next) + if(l->n->op == ODCLFUNC && l->n->curfn == N) + visit(l->n); + + for(l=all; l; l=l->next) + l->n->walkgen = 0; +} + +static uint32 +visit(Node *n) +{ + uint32 min, recursive; + NodeList *l, *block; + + if(n->walkgen > 0) { + // already visited + return n->walkgen; + } + + visitgen++; + n->walkgen = visitgen; + visitgen++; + min = visitgen; + + l = mal(sizeof *l); + l->next = stack; + l->n = n; + stack = l; + min = visitcodelist(n->nbody, min); + if((min == n->walkgen || min == n->walkgen+1) && n->curfn == N) { + // This node is the root of a strongly connected component. + + // The original min passed to visitcodelist was n->walkgen+1. + // If visitcodelist found its way back to n->walkgen, then this + // block is a set of mutually recursive functions. + // Otherwise it's just a lone function that does not recurse. + recursive = min == n->walkgen; + + // Remove connected component from stack. + // Mark walkgen so that future visits return a large number + // so as not to affect the caller's min. + block = stack; + for(l=stack; l->n != n; l=l->next) + l->n->walkgen = (uint32)~0U; + n->walkgen = (uint32)~0U; + stack = l->next; + l->next = nil; + + // Run escape analysis on this set of functions. + analyze(block, recursive); + } + + return min; +} + +static uint32 +visitcodelist(NodeList *l, uint32 min) +{ + for(; l; l=l->next) + min = visitcode(l->n, min); + return min; +} + +static uint32 +visitcode(Node *n, uint32 min) +{ + Node *fn; + uint32 m; + + if(n == N) + return min; + + min = visitcodelist(n->ninit, min); + min = visitcode(n->left, min); + min = visitcode(n->right, min); + min = visitcodelist(n->list, min); + min = visitcode(n->ntest, min); + min = visitcode(n->nincr, min); + min = visitcodelist(n->nbody, min); + min = visitcodelist(n->nelse, min); + min = visitcodelist(n->rlist, min); + + if(n->op == OCALLFUNC || n->op == OCALLMETH) { + fn = n->left; + if(n->op == OCALLMETH) + fn = n->left->right->sym->def; + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody) + if((m = visit(fn->defn)) < min) + min = m; + } + + if(n->op == OCLOSURE) + if((m = visit(n->closure)) < min) + min = m; + + return min; +} + +// An escape analysis pass for a set of functions. // // First escfunc, esc and escassign recurse over the ast of each // function to dig out flow(dst,src) edges between any @@ -22,75 +176,123 @@ // not escape, then new(T) can be rewritten into a stack allocation. // The same is true of slice literals. // -// If escape analysis is disabled (-s), this code is not used. +// If optimizations are disabled (-N), this code is not used. // Instead, the compiler assumes that any value whose address // is taken without being immediately dereferenced // needs to be moved to the heap, and new(T) and slice // literals are always real allocations. -#include <u.h> -#include <libc.h> -#include "go.h" +typedef struct EscState EscState; + +static void escfunc(EscState*, Node *func); +static void esclist(EscState*, NodeList *l); +static void esc(EscState*, Node *n); +static void escloopdepthlist(EscState*, NodeList *l); +static void escloopdepth(EscState*, Node *n); +static void escassign(EscState*, Node *dst, Node *src); +static void esccall(EscState*, Node*); +static void escflows(EscState*, Node *dst, Node *src); +static void escflood(EscState*, Node *dst); +static void escwalk(EscState*, int level, Node *dst, Node *src); +static void esctag(EscState*, Node *func); + +struct EscState { + // Fake node that all + // - return values and output variables + // - parameters on imported functions not marked 'safe' + // - assignments to global variables + // flow to. + Node theSink; + + NodeList* dsts; // all dst nodes + int loopdepth; // for detecting nested loop scopes + int pdepth; // for debug printing in recursions. + int dstcount, edgecount; // diagnostic + NodeList* noesc; // list of possible non-escaping nodes, for printing + int recursive; // recursive function or group of mutually recursive functions. +}; + +static Strlit *tags[16]; + +static Strlit* +mktag(int mask) +{ + Strlit *s; + char buf[40]; + + switch(mask&EscMask) { + case EscNone: + case EscReturn: + break; + default: + fatal("escape mktag"); + } -static void escfunc(Node *func); -static void esclist(NodeList *l); -static void esc(Node *n); -static void escloopdepthlist(NodeList *l); -static void escloopdepth(Node *n); -static void escassign(Node *dst, Node *src); -static void esccall(Node*); -static void escflows(Node *dst, Node *src); -static void escflood(Node *dst); -static void escwalk(int level, Node *dst, Node *src); -static void esctag(Node *func); - -// Fake node that all -// - return values and output variables -// - parameters on imported functions not marked 'safe' -// - assignments to global variables -// flow to. -static Node theSink; - -static NodeList* dsts; // all dst nodes -static int loopdepth; // for detecting nested loop scopes -static int pdepth; // for debug printing in recursions. -static Strlit* safetag; // gets slapped on safe parameters' field types for export -static int dstcount, edgecount; // diagnostic -static NodeList* noesc; // list of possible non-escaping nodes, for printing + mask >>= EscBits; -void -escapes(NodeList *all) + if(mask < nelem(tags) && tags[mask] != nil) + return tags[mask]; + + snprint(buf, sizeof buf, "esc:0x%x", mask); + s = strlit(buf); + if(mask < nelem(tags)) + tags[mask] = s; + return s; +} + +static int +parsetag(Strlit *note) { - NodeList *l; + int em; + + if(note == nil) + return EscUnknown; + if(strncmp(note->s, "esc:", 4) != 0) + return EscUnknown; + em = atoi(note->s + 4); + if (em == 0) + return EscNone; + return EscReturn | (em << EscBits); +} - theSink.op = ONAME; - theSink.orig = &theSink; - theSink.class = PEXTERN; - theSink.sym = lookup(".sink"); - theSink.escloopdepth = -1; +static void +analyze(NodeList *all, int recursive) +{ + NodeList *l; + EscState es, *e; + + memset(&es, 0, sizeof es); + e = &es; + e->theSink.op = ONAME; + e->theSink.orig = &e->theSink; + e->theSink.class = PEXTERN; + e->theSink.sym = lookup(".sink"); + e->theSink.escloopdepth = -1; + e->recursive = recursive; - safetag = strlit("noescape"); - noesc = nil; + for(l=all; l; l=l->next) + if(l->n->op == ODCLFUNC) + l->n->esc = EscFuncPlanned; // flow-analyze functions for(l=all; l; l=l->next) - if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) - escfunc(l->n); + if(l->n->op == ODCLFUNC) + escfunc(e, l->n); - // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); + // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount); - // visit the updstream of each dst, mark address nodes with + // visit the upstream of each dst, mark address nodes with // addrescapes, mark parameters unsafe - for(l = dsts; l; l=l->next) - escflood(l->n); + for(l = e->dsts; l; l=l->next) + escflood(e, l->n); // for all top level functions, tag the typenodes corresponding to the param nodes for(l=all; l; l=l->next) if(l->n->op == ODCLFUNC) - esctag(l->n); + esctag(e, l->n); if(debug['m']) { - for(l=noesc; l; l=l->next) + for(l=e->noesc; l; l=l->next) if(l->n->esc == EscNone) warnl(l->n->lineno, "%S %hN does not escape", (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S, @@ -100,14 +302,20 @@ escapes(NodeList *all) static void -escfunc(Node *func) +escfunc(EscState *e, Node *func) { - Node *savefn, *n; + Node *savefn; NodeList *ll; int saveld; - saveld = loopdepth; - loopdepth = 1; +// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":""); + + if(func->esc != 1) + fatal("repeat escfunc %N", func->nname); + func->esc = EscFuncStarted; + + saveld = e->loopdepth; + e->loopdepth = 1; savefn = curfn; curfn = func; @@ -116,63 +324,54 @@ escfunc(Node *func) continue; switch (ll->n->class) { case PPARAMOUT: - // output parameters flow to the sink - escflows(&theSink, ll->n); - ll->n->escloopdepth = loopdepth; + // out params are in a loopdepth between the sink and all local variables + ll->n->escloopdepth = 0; break; case PPARAM: if(ll->n->type && !haspointers(ll->n->type)) break; - ll->n->esc = EscNone; // prime for escflood later - noesc = list(noesc, ll->n); - ll->n->escloopdepth = loopdepth; + if(curfn->nbody == nil && !curfn->noescape) + ll->n->esc = EscHeap; + else + ll->n->esc = EscNone; // prime for escflood later + e->noesc = list(e->noesc, ll->n); + ll->n->escloopdepth = 1; break; } } - // walk will take the address of cvar->closure later and assign it to cvar. - // linking a fake oaddr node directly to the closure handles the case - // of the closure itself leaking. Following the flow of the value to th - // paramref is done in escflow, because if we did that here, it would look - // like the original is assigned out of its loop depth, whereas it's just - // assigned to something in an inner function. A paramref itself is never - // moved to the heap, only its original. - for(ll=curfn->cvars; ll; ll=ll->next) { - if(ll->n->op == OXXX) // see dcl.c:398 - continue; - - n = nod(OADDR, ll->n->closure, N); - n->lineno = ll->n->lineno; - typecheck(&n, Erv); - escassign(curfn, n); - } + // in a mutually recursive group we lose track of the return values + if(e->recursive) + for(ll=curfn->dcl; ll; ll=ll->next) + if(ll->n->op == ONAME && ll->n->class == PPARAMOUT) + escflows(e, &e->theSink, ll->n); - escloopdepthlist(curfn->nbody); - esclist(curfn->nbody); + escloopdepthlist(e, curfn->nbody); + esclist(e, curfn->nbody); curfn = savefn; - loopdepth = saveld; + e->loopdepth = saveld; } -// Mark labels that have no backjumps to them as not increasing loopdepth. +// Mark labels that have no backjumps to them as not increasing e->loopdepth. // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat // and set it to one of the following two. Then in esc we'll clear it again. static Label looping; static Label nonlooping; static void -escloopdepthlist(NodeList *l) +escloopdepthlist(EscState *e, NodeList *l) { for(; l; l=l->next) - escloopdepth(l->n); + escloopdepth(e, l->n); } static void -escloopdepth(Node *n) +escloopdepth(EscState *e, Node *n) { if(n == N) return; - escloopdepthlist(n->ninit); + escloopdepthlist(e, n->ninit); switch(n->op) { case OLABEL: @@ -194,29 +393,30 @@ escloopdepth(Node *n) break; } - escloopdepth(n->left); - escloopdepth(n->right); - escloopdepthlist(n->list); - escloopdepth(n->ntest); - escloopdepth(n->nincr); - escloopdepthlist(n->nbody); - escloopdepthlist(n->nelse); - escloopdepthlist(n->rlist); + escloopdepth(e, n->left); + escloopdepth(e, n->right); + escloopdepthlist(e, n->list); + escloopdepth(e, n->ntest); + escloopdepth(e, n->nincr); + escloopdepthlist(e, n->nbody); + escloopdepthlist(e, n->nelse); + escloopdepthlist(e, n->rlist); } static void -esclist(NodeList *l) +esclist(EscState *e, NodeList *l) { for(; l; l=l->next) - esc(l->n); + esc(e, l->n); } static void -esc(Node *n) +esc(EscState *e, Node *n) { int lno; NodeList *ll, *lr; + Node *a; if(n == N) return; @@ -224,33 +424,30 @@ esc(Node *n) lno = setlineno(n); if(n->op == OFOR || n->op == ORANGE) - loopdepth++; - - if(n->op == OCLOSURE) { - escfunc(n); - } else { - esc(n->left); - esc(n->right); - esc(n->ntest); - esc(n->nincr); - esclist(n->ninit); - esclist(n->nbody); - esclist(n->nelse); - esclist(n->list); - esclist(n->rlist); - } + e->loopdepth++; + + esc(e, n->left); + esc(e, n->right); + esc(e, n->ntest); + esc(e, n->nincr); + esclist(e, n->ninit); + esclist(e, n->nbody); + esclist(e, n->nelse); + esclist(e, n->list); + esclist(e, n->rlist); + if(n->op == OFOR || n->op == ORANGE) - loopdepth--; + e->loopdepth--; if(debug['m'] > 1) - print("%L:[%d] %S esc: %N\n", lineno, loopdepth, + print("%L:[%d] %S esc: %N\n", lineno, e->loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, n); switch(n->op) { case ODCL: // Record loop depth at declaration. if(n->left) - n->left->escloopdepth = loopdepth; + n->left->escloopdepth = e->loopdepth; break; case OLABEL: @@ -260,18 +457,19 @@ esc(Node *n) } else if(n->left->sym->label == &looping) { if(debug['m'] > 1) print("%L: %N looping label\n", lineno, n); - loopdepth++; + e->loopdepth++; } // See case OLABEL in escloopdepth above // else if(n->left->sym->label == nil) - // fatal("escape anaylysis missed or messed up a label: %+N", n); + // fatal("escape analysis missed or messed up a label: %+N", n); n->left->sym->label = nil; + break; case ORANGE: // Everything but fixed array is a dereference. if(isfixedarray(n->type) && n->list->next) - escassign(n->list->next->n, n->right); + escassign(e, n->list->next->n, n->right); break; case OSWITCH: @@ -279,123 +477,157 @@ esc(Node *n) for(ll=n->list; ll; ll=ll->next) { // cases // ntest->right is the argument of the .(type), // ll->n->nname is the variable per case - escassign(ll->n->nname, n->ntest->right); + escassign(e, ll->n->nname, n->ntest->right); } } break; case OAS: case OASOP: - escassign(n->left, n->right); + escassign(e, n->left, n->right); break; case OAS2: // x,y = a,b if(count(n->list) == count(n->rlist)) for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) - escassign(ll->n, lr->n); + escassign(e, ll->n, lr->n); break; case OAS2RECV: // v, ok = <-ch case OAS2MAPR: // v, ok = m[k] case OAS2DOTTYPE: // v, ok = x.(type) - escassign(n->list->n, n->rlist->n); + escassign(e, n->list->n, n->rlist->n); break; case OSEND: // ch <- x - escassign(&theSink, n->right); + escassign(e, &e->theSink, n->right); break; case ODEFER: - if(loopdepth == 1) // top level + if(e->loopdepth == 1) // top level break; // arguments leak out of scope // TODO: leak to a dummy node instead // fallthrough case OPROC: // go f(x) - f and x escape - escassign(&theSink, n->left->left); - escassign(&theSink, n->left->right); // ODDDARG for call + escassign(e, &e->theSink, n->left->left); + escassign(e, &e->theSink, n->left->right); // ODDDARG for call for(ll=n->left->list; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); + break; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + esccall(e, n); + break; + + case OAS2FUNC: // x,y = f() + // esccall already done on n->rlist->n. tie it's escretval to n->list + lr=n->rlist->n->escretval; + for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next) + escassign(e, ll->n, lr->n); + if(lr || ll) + fatal("esc oas2func"); break; case ORETURN: - for(ll=n->list; ll; ll=ll->next) - escassign(&theSink, ll->n); + ll=n->list; + if(count(n->list) == 1 && curfn->type->outtuple > 1) { + // OAS2FUNC in disguise + // esccall already done on n->list->n + // tie n->list->n->escretval to curfn->dcl PPARAMOUT's + ll = n->list->n->escretval; + } + + for(lr = curfn->dcl; lr && ll; lr=lr->next) { + if (lr->n->op != ONAME || lr->n->class != PPARAMOUT) + continue; + escassign(e, lr->n, ll->n); + ll = ll->next; + } + if (ll != nil) + fatal("esc return list"); break; case OPANIC: // Argument could leak through recover. - escassign(&theSink, n->left); + escassign(e, &e->theSink, n->left); break; case OAPPEND: if(!n->isddd) for(ll=n->list->next; ll; ll=ll->next) - escassign(&theSink, ll->n); // lose track of assign to dereference - break; - - case OCALLMETH: - case OCALLFUNC: - case OCALLINTER: - esccall(n); + escassign(e, &e->theSink, ll->n); // lose track of assign to dereference break; case OCONV: case OCONVNOP: case OCONVIFACE: - escassign(n, n->left); + escassign(e, n, n->left); break; case OARRAYLIT: if(isslice(n->type)) { n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Values make it to memory, lose track. for(ll=n->list; ll; ll=ll->next) - escassign(&theSink, ll->n->right); + escassign(e, &e->theSink, ll->n->right); } else { // Link values to array. for(ll=n->list; ll; ll=ll->next) - escassign(n, ll->n->right); + escassign(e, n, ll->n->right); } break; case OSTRUCTLIT: // Link values to struct. for(ll=n->list; ll; ll=ll->next) - escassign(n, ll->n->right); + escassign(e, n, ll->n->right); break; case OPTRLIT: n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Contents make it to memory, lose track. - escassign(&theSink, n->left); + escassign(e, &e->theSink, n->left); break; case OMAPLIT: n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); - n->escloopdepth = loopdepth; + e->noesc = list(e->noesc, n); + n->escloopdepth = e->loopdepth; // Keys and values make it to memory, lose track. for(ll=n->list; ll; ll=ll->next) { - escassign(&theSink, ll->n->left); - escassign(&theSink, ll->n->right); + escassign(e, &e->theSink, ll->n->left); + escassign(e, &e->theSink, ll->n->right); } break; case OCLOSURE: + // Link addresses of captured variables to closure. + for(ll=n->cvars; ll; ll=ll->next) { + if(ll->n->op == OXXX) // unnamed out argument; see dcl.c:/^funcargs + continue; + a = nod(OADDR, ll->n->closure, N); + a->lineno = ll->n->lineno; + a->escloopdepth = e->loopdepth; + typecheck(&a, Erv); + escassign(e, n, a); + } + // fallthrough case OADDR: case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: case ONEW: - n->escloopdepth = loopdepth; + n->escloopdepth = e->loopdepth; n->esc = EscNone; // until proven otherwise - noesc = list(noesc, n); + e->noesc = list(e->noesc, n); break; } @@ -407,21 +639,22 @@ esc(Node *n) // evaluated in curfn. For expr==nil, dst must still be examined for // evaluations inside it (e.g *f(x) = y) static void -escassign(Node *dst, Node *src) +escassign(EscState *e, Node *dst, Node *src) { int lno; + NodeList *ll; if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX) return; if(debug['m'] > 1) - print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, loopdepth, + print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, e->loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src); setlineno(dst); // Analyze lhs of assignment. - // Replace dst with theSink if we can't track it. + // Replace dst with e->theSink if we can't track it. switch(dst->op) { default: dump("dst", dst); @@ -438,31 +671,31 @@ escassign(Node *dst, Node *src) case ONAME: if(dst->class == PEXTERN) - dst = &theSink; + dst = &e->theSink; break; case ODOT: // treat "dst.x = src" as "dst = src" - escassign(dst->left, src); + escassign(e, dst->left, src); return; case OINDEX: if(isfixedarray(dst->left->type)) { - escassign(dst->left, src); + escassign(e, dst->left, src); return; } - dst = &theSink; // lose track of dereference + dst = &e->theSink; // lose track of dereference break; case OIND: case ODOTPTR: - dst = &theSink; // lose track of dereference + dst = &e->theSink; // lose track of dereference break; case OINDEXMAP: // lose track of key and value - escassign(&theSink, dst->right); - dst = &theSink; + escassign(e, &e->theSink, dst->right); + dst = &e->theSink; break; } lno = setlineno(src); - pdepth++; + e->pdepth++; switch(src->op) { case OADDR: // dst = &x @@ -480,7 +713,16 @@ escassign(Node *dst, Node *src) case OMAKESLICE: case ONEW: case OCLOSURE: - escflows(dst, src); + escflows(e, dst, src); + break; + + case OCALLMETH: + case OCALLFUNC: + case OCALLINTER: + // Flowing multiple returns to a single dst happens when + // analyzing "go f(g())": here g() flows to sink (issue 4529). + for(ll=src->escretval; ll; ll=ll->next) + escflows(e, dst, ll->n); break; case ODOT: @@ -498,18 +740,18 @@ escassign(Node *dst, Node *src) case OSLICE: case OSLICEARR: // Conversions, field access, slice all preserve the input value. - escassign(dst, src->left); + escassign(e, dst, src->left); break; case OAPPEND: // Append returns first argument. - escassign(dst, src->list->n); + escassign(e, dst, src->list->n); break; case OINDEX: // Index of array preserves input value. if(isfixedarray(src->left->type)) - escassign(dst, src->left); + escassign(e, dst, src->left); break; case OADD: @@ -529,29 +771,49 @@ escassign(Node *dst, Node *src) // Might be pointer arithmetic, in which case // the operands flow into the result. // TODO(rsc): Decide what the story is here. This is unsettling. - escassign(dst, src->left); - escassign(dst, src->right); + escassign(e, dst, src->left); + escassign(e, dst, src->right); break; - } - pdepth--; + e->pdepth--; lineno = lno; } +static void +escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src) +{ + int em; + + em = parsetag(note); + + if(em == EscUnknown) { + escassign(e, &e->theSink, src); + return; + } + + for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next) + if(em & 1) + escassign(e, dsts->n, src); + + if (em != 0 && dsts == nil) + fatal("corrupt esc tag %Z or messed up escretval list\n", note); +} -// This is a bit messier than fortunate, pulled out of escassign's big +// This is a bit messier than fortunate, pulled out of esc's big // switch for clarity. We either have the paramnodes, which may be -// connected to other things throug flows or we have the parameter type +// connected to other things through flows or we have the parameter type // nodes, which may be marked "noescape". Navigating the ast is slightly // different for methods vs plain functions and for imported vs // this-package static void -esccall(Node *n) +esccall(EscState *e, Node *n) { NodeList *ll, *lr; Node *a, *fn, *src; Type *t, *fntype; + char buf[40]; + int i; fn = N; switch(n->op) { @@ -579,74 +841,96 @@ esccall(Node *n) ll = n->list; if(n->list != nil && n->list->next == nil) { a = n->list->n; - if(a->type->etype == TSTRUCT && a->type->funarg) { - // f(g()). - // Since f's arguments are g's results and - // all function results escape, we're done. - ll = nil; - } + if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()). + ll = a->escretval; } - if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype) { - // Local function. Incorporate into flow graph. + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) { + // function in same mutually recursive group. Incorporate into flow graph. +// print("esc local fn: %N\n", fn->ntype); + if(fn->defn->esc == EscFuncUnknown || n->escretval != nil) + fatal("graph inconsistency"); + + // set up out list on this call node + for(lr=fn->ntype->rlist; lr; lr=lr->next) + n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT) // Receiver. if(n->op != OCALLFUNC) - escassign(fn->ntype->left->left, n->left->left); + escassign(e, fn->ntype->left->left, n->left->left); for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) { src = ll->n; if(lr->n->isddd && !n->isddd) { // Introduce ODDDARG node to represent ... allocation. src = nod(ODDDARG, N, N); - src->escloopdepth = loopdepth; + src->escloopdepth = e->loopdepth; src->lineno = n->lineno; src->esc = EscNone; // until we find otherwise - noesc = list(noesc, src); + e->noesc = list(e->noesc, src); n->right = src; } if(lr->n->left != N) - escassign(lr->n->left, src); + escassign(e, lr->n->left, src); if(src != ll->n) break; } // "..." arguments are untracked for(; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); + return; } - // Imported function. Use the escape tags. - if(n->op != OCALLFUNC) { - t = getthisx(fntype)->type; - if(!t->note || strcmp(t->note->s, safetag->s) != 0) - escassign(&theSink, n->left->left); + // Imported or completely analyzed function. Use the escape tags. + if(n->escretval != nil) + fatal("esc already decorated call %+N\n", n); + + // set up out list on this call node with dummy auto ONAMES in the current (calling) function. + i = 0; + for(t=getoutargx(fntype)->type; t; t=t->down) { + src = nod(ONAME, N, N); + snprint(buf, sizeof buf, ".dum%d", i++); + src->sym = lookup(buf); + src->type = t->type; + src->class = PAUTO; + src->curfn = curfn; + src->escloopdepth = e->loopdepth; + src->used = 1; + src->lineno = n->lineno; + n->escretval = list(n->escretval, src); } + +// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); + + // Receiver. + if(n->op != OCALLFUNC) + escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left); + for(t=getinargx(fntype)->type; ll; ll=ll->next) { src = ll->n; if(t->isddd && !n->isddd) { // Introduce ODDDARG node to represent ... allocation. src = nod(ODDDARG, N, N); - src->escloopdepth = loopdepth; + src->escloopdepth = e->loopdepth; src->lineno = n->lineno; src->esc = EscNone; // until we find otherwise - noesc = list(noesc, src); + e->noesc = list(e->noesc, src); n->right = src; } - if(!t->note || strcmp(t->note->s, safetag->s) != 0) - escassign(&theSink, src); + escassignfromtag(e, t->note, n->escretval, src); if(src != ll->n) break; t = t->down; } // "..." arguments are untracked for(; ll; ll=ll->next) - escassign(&theSink, ll->n); + escassign(e, &e->theSink, ll->n); } // Store the link src->dst in dst, throwing out some quick wins. static void -escflows(Node *dst, Node *src) +escflows(EscState *e, Node *dst, Node *src) { if(dst == nil || src == nil || dst == src) return; @@ -659,10 +943,10 @@ escflows(Node *dst, Node *src) print("%L::flows:: %hN <- %hN\n", lineno, dst, src); if(dst->escflowsrc == nil) { - dsts = list(dsts, dst); - dstcount++; + e->dsts = list(e->dsts, dst); + e->dstcount++; } - edgecount++; + e->edgecount++; dst->escflowsrc = list(dst->escflowsrc, src); } @@ -673,11 +957,11 @@ escflows(Node *dst, Node *src) // so this address doesn't leak (yet). // If level == 0, it means the /value/ of this node can reach the root of this flood. // so if this node is an OADDR, it's argument should be marked as escaping iff -// it's currfn/loopdepth are different from the flood's root. +// it's currfn/e->loopdepth are different from the flood's root. // Once an object has been moved to the heap, all of it's upstream should be considered // escaping to the global scope. static void -escflood(Node *dst) +escflood(EscState *e, Node *dst) { NodeList *l; @@ -696,45 +980,71 @@ escflood(Node *dst) for(l = dst->escflowsrc; l; l=l->next) { walkgen++; - escwalk(0, dst, l->n); + escwalk(e, 0, dst, l->n); } } +// There appear to be some loops in the escape graph, causing +// arbitrary recursion into deeper and deeper levels. +// Cut this off safely by making minLevel sticky: once you +// get that deep, you cannot go down any further but you also +// cannot go up any further. This is a conservative fix. +// Making minLevel smaller (more negative) would handle more +// complex chains of indirections followed by address-of operations, +// at the cost of repeating the traversal once for each additional +// allowed level when a loop is encountered. Using -2 suffices to +// pass all the tests we have written so far, which we assume matches +// the level of complexity we want the escape analysis code to handle. +#define MinLevel (-2) + static void -escwalk(int level, Node *dst, Node *src) +escwalk(EscState *e, int level, Node *dst, Node *src) { NodeList *ll; - int leaks; + int leaks, newlevel; - if(src->walkgen == walkgen) + if(src->walkgen == walkgen && src->esclevel <= level) return; src->walkgen = walkgen; + src->esclevel = level; if(debug['m']>1) print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n", - level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src, + level, e->pdepth, e->pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src, (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth); - pdepth++; + e->pdepth++; + + // Input parameter flowing to output parameter? + if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) { + if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) { + if(src->esc != EscScope && src->esc != EscHeap) { + if(debug['m']) + warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym); + if((src->esc&EscMask) != EscReturn) + src->esc = EscReturn; + src->esc |= 1<<((dst->vargen-1) + EscBits); + goto recurse; + } + } + } leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); switch(src->op) { case ONAME: - if(src->class == PPARAM && leaks && src->esc == EscNone) { + if(src->class == PPARAM && leaks && src->esc != EscHeap) { src->esc = EscScope; if(debug['m']) warnl(src->lineno, "leaking param: %hN", src); } - // handle the missing flow ref <- orig - // a paramref is automagically dereferenced, and taking its - // address produces the address of the original, so all we have to do here - // is keep track of the value flow, so level is unchanged. - // alternatively, we could have substituted PPARAMREFs with their ->closure in esc/escassign/flow, + + // Treat a PPARAMREF closure variable as equivalent to the + // original variable. if(src->class == PPARAMREF) { if(leaks && debug['m']) warnl(src->lineno, "leaking closure reference %hN", src); - escwalk(level, dst, src->closure); + escwalk(e, level, dst, src->closure); } break; @@ -746,7 +1056,10 @@ escwalk(int level, Node *dst, Node *src) if(debug['m']) warnl(src->lineno, "%hN escapes to heap", src); } - escwalk(level-1, dst, src->left); + newlevel = level; + if(level > MinLevel) + newlevel--; + escwalk(e, newlevel, dst, src->left); break; case OARRAYLIT: @@ -767,32 +1080,53 @@ escwalk(int level, Node *dst, Node *src) } break; + case ODOT: + escwalk(e, level, dst, src->left); + break; + case OINDEX: - if(isfixedarray(src->type)) + if(isfixedarray(src->left->type)) { + escwalk(e, level, dst, src->left); break; + } // fall through case OSLICE: case ODOTPTR: case OINDEXMAP: case OIND: - escwalk(level+1, dst, src->left); + newlevel = level; + if(level > MinLevel) + newlevel++; + escwalk(e, newlevel, dst, src->left); } +recurse: for(ll=src->escflowsrc; ll; ll=ll->next) - escwalk(level, dst, ll->n); + escwalk(e, level, dst, ll->n); - pdepth--; + e->pdepth--; } static void -esctag(Node *func) +esctag(EscState *e, Node *func) { Node *savefn; NodeList *ll; + Type *t; + + USED(e); + func->esc = EscFuncTagged; - // External functions must be assumed unsafe. - if(func->nbody == nil) + // External functions are assumed unsafe, + // unless //go:noescape is given before the declaration. + if(func->nbody == nil) { + if(func->noescape) { + for(t=getinargx(func->type)->type; t; t=t->down) + if(haspointers(t->type)) + t->note = mktag(EscNone); + } return; + } savefn = curfn; curfn = func; @@ -801,10 +1135,12 @@ esctag(Node *func) if(ll->n->op != ONAME || ll->n->class != PPARAM) continue; - switch (ll->n->esc) { + switch (ll->n->esc&EscMask) { case EscNone: // not touched by escflood + case EscReturn: if(haspointers(ll->n->type)) // don't bother tagging for scalars - ll->n->paramfld->note = safetag; + ll->n->paramfld->note = mktag(ll->n->esc); + break; case EscHeap: // touched by escflood, moved to heap case EscScope: // touched by escflood, value leaves scope break; diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index bbed8ae36..b7311665a 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -22,22 +22,8 @@ exportsym(Node *n) } n->sym->flags |= SymExport; - exportlist = list(exportlist, n); -} - -// Mark n's symbol as package-local -static void -packagesym(Node *n) -{ - if(n == N || n->sym == S) - return; - if(n->sym->flags & (SymExport|SymPackage)) { - if(n->sym->flags & SymExport) - yyerror("export/package mismatch: %S", n->sym); - return; - } - n->sym->flags |= SymPackage; - + if(debug['E']) + print("export symbol %S\n", n->sym); exportlist = list(exportlist, n); } @@ -58,6 +44,18 @@ initname(char *s) return strcmp(s, "init") == 0; } +// exportedsym returns whether a symbol will be visible +// to files that import our package. +static int +exportedsym(Sym *sym) +{ + // Builtins are visible everywhere. + if(sym->pkg == builtinpkg || sym->origpkg == builtinpkg) + return 1; + + return sym->pkg == localpkg && exportname(sym->name); +} + void autoexport(Node *n, int ctxt) { @@ -67,10 +65,9 @@ autoexport(Node *n, int ctxt) return; if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left) // method return; - if(exportname(n->sym->name) || initname(n->sym->name)) + // -A is for cmd/gc/mkbuiltin script, so export everything + if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name)) exportsym(n); - else - packagesym(n); } static void @@ -104,36 +101,60 @@ reexportdep(Node *n) if(!n) return; -// print("reexportdep %+hN\n", n); + //print("reexportdep %+hN\n", n); switch(n->op) { case ONAME: switch(n->class&~PHEAP) { case PFUNC: // methods will be printed along with their type + // nodes for T.Method expressions + if(n->left && n->left->op == OTYPE) + break; + // nodes for method calls. if(!n->type || n->type->thistuple > 0) break; // fallthrough case PEXTERN: - if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + if(n->sym && !exportedsym(n->sym)) { + if(debug['E']) + print("reexport name %S\n", n->sym); exportlist = list(exportlist, n); + } } break; + case ODCL: + // Local variables in the bodies need their type. + t = n->left->type; + if(t != types[t->etype] && t != idealbool && t != idealstring) { + if(isptr[t->etype]) + t = t->type; + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport type %S from declaration\n", t->sym); + exportlist = list(exportlist, t->sym->def); + } + } + break; case OLITERAL: t = n->type; if(t != types[n->type->etype] && t != idealbool && t != idealstring) { if(isptr[t->etype]) t = t->type; - if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { -// print("reexport literal type %+hN\n", t->sym->def); + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport literal type %S\n", t->sym); exportlist = list(exportlist, t->sym->def); } } // fallthrough case OTYPE: - if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg) + if(n->sym && !exportedsym(n->sym)) { + if(debug['E']) + print("reexport literal/type %S\n", n->sym); exportlist = list(exportlist, n); + } break; // for operations that need a type when rendered, put the type on the export list. @@ -141,13 +162,15 @@ reexportdep(Node *n) case OCONVIFACE: case OCONVNOP: case ODOTTYPE: + case ODOTTYPE2: case OSTRUCTLIT: case OPTRLIT: t = n->type; if(!t->sym && t->type) t = t->type; - if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) { -// print("reexport convnop %+hN\n", t->sym->def); + if(t && t->sym && t->sym->def && !exportedsym(t->sym)) { + if(debug['E']) + print("reexport type for convnop %S\n", t->sym); exportlist = list(exportlist, t->sym->def); } break; @@ -207,10 +230,11 @@ dumpexportvar(Sym *s) // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package if(debug['l'] < 2) typecheckinl(n); - Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl); + // NOTE: The space after %#S here is necessary for ld's export data parser. + Bprint(bout, "\tfunc %#S %#hT { %#H }\n", s, t, n->inl); reexportdeplist(n->inl); } else - Bprint(bout, "\tfunc %#S%#hT\n", s, t); + Bprint(bout, "\tfunc %#S %#hT\n", s, t); } else Bprint(bout, "\tvar %#S %#T\n", s, t); } @@ -262,15 +286,17 @@ dumpexporttype(Type *t) Bprint(bout, "\ttype %#S %#lT\n", t->sym, t); for(i=0; i<n; i++) { f = m[i]; + if(f->nointerface) + Bprint(bout, "\t//go:nointerface\n"); if (f->type->nname && f->type->nname->inl) { // nname was set by caninl // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet. // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package if(debug['l'] < 2) typecheckinl(f->type->nname); - Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl); + Bprint(bout, "\tfunc (%#T) %#hhS %#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl); reexportdeplist(f->type->nname->inl); } else - Bprint(bout, "\tfunc (%#T) %#hhS%#hT\n", getthisx(f->type)->type, f->sym, f->type); + Bprint(bout, "\tfunc (%#T) %#hhS %#hT\n", getthisx(f->type)->type, f->sym, f->type); } } @@ -349,8 +375,12 @@ dumpexport(void) Sym* importsym(Sym *s, int op) { - if(s->def != N && s->def->op != op) - redeclare(s, "during import"); + char *pkgstr; + + if(s->def != N && s->def->op != op) { + pkgstr = smprint("during import \"%Z\"", importpkg->path); + redeclare(s, pkgstr); + } // mark the symbol so it is not reexported if(s->def == N) { @@ -389,6 +419,8 @@ importimport(Sym *s, Strlit *z) // human-readable messages. Pkg *p; + if(isbadimport(z)) + errorexit(); p = mkpkg(z); if(p->name == nil) { p->name = s->name; diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 5672c0010..ab81e6c88 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -228,6 +228,7 @@ goopnames[] = [ORANGE] = "range", [OREAL] = "real", [ORECV] = "<-", + [ORECOVER] = "recover", [ORETURN] = "return", [ORSH] = ">>", [OSELECT] = "select", @@ -289,7 +290,7 @@ Jconv(Fmt *fp) fmtprint(fp, " l(%d)", n->lineno); if(!c && n->xoffset != BADWIDTH) - fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta); + fmtprint(fp, " x(%lld%+lld)", n->xoffset, n->stkdelta); if(n->class != 0) { s = ""; @@ -361,6 +362,8 @@ Vconv(Fmt *fp) switch(v->ctype) { case CTINT: + if((fp->flags & FmtSharp) || fmtmode == FExp) + return fmtprint(fp, "%#B", v->u.xval); return fmtprint(fp, "%B", v->u.xval); case CTRUNE: x = mpgetfix(v->u.xval); @@ -377,8 +380,14 @@ Vconv(Fmt *fp) return fmtprint(fp, "%#F", v->u.fval); case CTCPLX: if((fp->flags & FmtSharp) || fmtmode == FExp) - return fmtprint(fp, "(%F+%F)", &v->u.cval->real, &v->u.cval->imag); - return fmtprint(fp, "(%#F + %#Fi)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%F+%Fi)", &v->u.cval->real, &v->u.cval->imag); + if(mpcmpfltc(&v->u.cval->real, 0) == 0) + return fmtprint(fp, "%#Fi", &v->u.cval->imag); + if(mpcmpfltc(&v->u.cval->imag, 0) == 0) + return fmtprint(fp, "%#F", &v->u.cval->real); + if(mpcmpfltc(&v->u.cval->imag, 0) < 0) + return fmtprint(fp, "(%#F%#Fi)", &v->u.cval->real, &v->u.cval->imag); + return fmtprint(fp, "(%#F+%#Fi)", &v->u.cval->real, &v->u.cval->imag); case CTSTR: return fmtprint(fp, "\"%Z\"", v->u.sval); case CTBOOL: @@ -388,7 +397,7 @@ Vconv(Fmt *fp) case CTNIL: return fmtstrcpy(fp, "nil"); } - return fmtprint(fp, "<%d>", v->ctype); + return fmtprint(fp, "<ctype=%d>", v->ctype); } // Fmt "%Z": escaped string literals @@ -516,6 +525,8 @@ symfmt(Fmt *fp, Sym *s) return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym case FExp: + if(s->name && s->name[0] == '.') + fatal("exporting synthetic symbol %s", s->name); if(s->pkg != builtinpkg) return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name); } @@ -711,12 +722,21 @@ typefmt(Fmt *fp, Type *t) case TFIELD: if(!(fp->flags&FmtShort)) { s = t->sym; - // Take the name from the original, lest we substituted it with .anon%d - if (t->nname && (fmtmode == FErr || fmtmode == FExp)) - s = t->nname->orig->sym; + + // Take the name from the original, lest we substituted it with ~anon%d + if ((fmtmode == FErr || fmtmode == FExp) && t->nname != N) { + if(t->nname->orig != N) { + s = t->nname->orig->sym; + if(s != S && s->name[0] == '~') + s = S; + } else + s = S; + } if(s != S && !t->embedded) { - if(fp->flags&FmtLong) + if(t->funarg) + fmtprint(fp, "%N ", t->nname); + else if(fp->flags&FmtLong) fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg) else fmtprint(fp, "%S ", s); @@ -794,6 +814,15 @@ stmtfmt(Fmt *f, Node *n) switch(n->op){ case ODCL: + if(fmtmode == FExp) { + switch(n->left->class&~PHEAP) { + case PPARAM: + case PPARAMOUT: + case PAUTO: + fmtprint(f, "var %N %T", n->left, n->left->type); + goto ret; + } + } fmtprint(f, "var %S %T", n->left->sym, n->left->type); break; @@ -805,6 +834,12 @@ stmtfmt(Fmt *f, Node *n) break; case OAS: + // Don't export "v = <N>" initializing statements, hope they're always + // preceded by the DCL which will be re-parsed and typecheck to reproduce + // the "v = <N>" again. + if(fmtmode == FExp && n->right == N) + break; + if(n->colas && !complexinit) fmtprint(f, "%N := %N", n->left, n->right); else @@ -925,6 +960,7 @@ stmtfmt(Fmt *f, Node *n) break; } +ret: if(extrablock) fmtstrcpy(f, "}"); @@ -962,7 +998,6 @@ static int opprec[] = { [OPAREN] = 8, [OPRINTN] = 8, [OPRINT] = 8, - [ORECV] = 8, [ORUNESTR] = 8, [OSTRARRAYBYTE] = 8, [OSTRARRAYRUNE] = 8, @@ -994,6 +1029,7 @@ static int opprec[] = { [OMINUS] = 7, [OADDR] = 7, [OIND] = 7, + [ORECV] = 7, [OMUL] = 6, [ODIV] = 6, @@ -1055,8 +1091,8 @@ static int exprfmt(Fmt *f, Node *n, int prec) { int nprec; + int ptrlit; NodeList *l; - Type *t; while(n && n->implicit && (n->op == OIND || n->op == OADDR)) n = n->left; @@ -1084,9 +1120,9 @@ exprfmt(Fmt *f, Node *n, int prec) case OLITERAL: // this is a bit of a mess if(fmtmode == FErr && n->sym != S) return fmtprint(f, "%S", n->sym); - if(n->val.ctype == CTNIL) - n = n->orig; // if this node was a nil decorated with at type, print the original naked nil - if(n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { + if(n->val.ctype == CTNIL && n->orig != N && n->orig != n) + return exprfmt(f, n->orig, prec); + if(n->type != T && n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv)) @@ -1097,6 +1133,15 @@ exprfmt(Fmt *f, Node *n, int prec) return fmtprint(f, "%V", &n->val); case ONAME: + // Special case: name used as local variable in export. + switch(n->class&~PHEAP){ + case PAUTO: + case PPARAM: + case PPARAMOUT: + if(fmtmode == FExp && n->sym && !isblanksym(n->sym) && n->vargen > 0) + return fmtprint(f, "%S·%d", n->sym, n->vargen); + } + // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. // These nodes have the special property that they are names with a left OTYPE and a right ONAME. @@ -1152,12 +1197,25 @@ exprfmt(Fmt *f, Node *n, int prec) case OCLOSURE: if(fmtmode == FErr) return fmtstrcpy(f, "func literal"); - return fmtprint(f, "%T { %H }", n->type, n->nbody); + if(n->nbody) + return fmtprint(f, "%T { %H }", n->type, n->nbody); + return fmtprint(f, "%T { %H }", n->type, n->closure->nbody); case OCOMPLIT: - if(fmtmode == FErr) + ptrlit = n->right != N && n->right->implicit && n->right->type && isptr[n->right->type->etype]; + if(fmtmode == FErr) { + if(n->right != N && n->right->type != T && !n->implicit) { + if(ptrlit) + return fmtprint(f, "&%T literal", n->right->type->type); + else + return fmtprint(f, "%T literal", n->right->type); + } return fmtstrcpy(f, "composite literal"); - return fmtprint(f, "%N{ %,H }", n->right, n->list); + } + if(fmtmode == FExp && ptrlit) + // typecheck has overwritten OIND by OTYPE with pointer type. + return fmtprint(f, "&%T{ %,H }", n->right->type->type, n->list); + return fmtprint(f, "(%N{ %,H })", n->right, n->list); case OPTRLIT: if(fmtmode == FExp && n->left->implicit) @@ -1168,24 +1226,18 @@ exprfmt(Fmt *f, Node *n, int prec) if(fmtmode == FExp) { // requires special handling of field names if(n->implicit) fmtstrcpy(f, "{"); - else - fmtprint(f, "%T{", n->type); + else + fmtprint(f, "(%T{", n->type); for(l=n->list; l; l=l->next) { - // another special case: if n->left is an embedded field of builtin type, - // it needs to be non-qualified. Can't figure that out in %S, so do it here - if(l->n->left->type->embedded) { - t = l->n->left->type->type; - if(t->sym == S) - t = t->type; - fmtprint(f, " %T:%N", t, l->n->right); - } else - fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); + fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right); if(l->next) fmtstrcpy(f, ","); else fmtstrcpy(f, " "); } + if(!n->implicit) + return fmtstrcpy(f, "})"); return fmtstrcpy(f, "}"); } // fallthrough @@ -1196,11 +1248,16 @@ exprfmt(Fmt *f, Node *n, int prec) return fmtprint(f, "%T literal", n->type); if(fmtmode == FExp && n->implicit) return fmtprint(f, "{ %,H }", n->list); - return fmtprint(f, "%T{ %,H }", n->type, n->list); + return fmtprint(f, "(%T{ %,H })", n->type, n->list); case OKEY: - if(n->left && n->right) - return fmtprint(f, "%N:%N", n->left, n->right); + if(n->left && n->right) { + if(fmtmode == FExp && n->left->type && n->left->type->etype == TFIELD) { + // requires special handling of field names + return fmtprint(f, "%hhS:%N", n->left->sym, n->right); + } else + return fmtprint(f, "%N:%N", n->left, n->right); + } if(!n->left && n->right) return fmtprint(f, ":%N", n->right); if(n->left && !n->right) @@ -1260,6 +1317,7 @@ exprfmt(Fmt *f, Node *n, int prec) case OMAKE: case ONEW: case OPANIC: + case ORECOVER: case OPRINT: case OPRINTN: if(n->left) @@ -1408,6 +1466,7 @@ nodedump(Fmt *fp, Node *n) fmtprint(fp, "%O%J", n->op, n); break; case OREGISTER: + case OINDREG: fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n); break; case OLITERAL: @@ -1419,6 +1478,10 @@ nodedump(Fmt *fp, Node *n) fmtprint(fp, "%O-%S%J", n->op, n->sym, n); else fmtprint(fp, "%O%J", n->op, n); + if(recur && n->type == T && n->ntype) { + indent(fp); + fmtprint(fp, "%O-ntype%N", n->op, n->ntype); + } break; case OASOP: fmtprint(fp, "%O-%O%J", n->op, n->etype, n); @@ -1629,11 +1692,11 @@ fmtinstallgo(void) void dumplist(char *s, NodeList *l) { - print("%s\n%+H\n", s, l); + print("%s%+H\n", s, l); } void dump(char *s, Node *n) { - print("%s [%p]\n%+N\n", s, n, n); + print("%s [%p]%+N\n", s, n, n); } diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 96e7b526c..5f03d9476 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -266,6 +266,8 @@ gen(Node *n) Label *lab; int32 wasregalloc; +//dump("gen", n); + lno = setlineno(n); wasregalloc = anyregalloc(); @@ -279,7 +281,7 @@ gen(Node *n) switch(n->op) { default: - fatal("gen: unknown op %N", n); + fatal("gen: unknown op %+hN", n); break; case OCASE: @@ -394,7 +396,7 @@ gen(Node *n) } gen(n->nincr); // contin: incr patch(p1, pc); // test: - bgen(n->ntest, 0, breakpc); // if(!test) goto break + bgen(n->ntest, 0, -1, breakpc); // if(!test) goto break genlist(n->nbody); // body gjmp(continpc); patch(breakpc, pc); // done: @@ -410,7 +412,7 @@ gen(Node *n) p1 = gjmp(P); // goto test p2 = gjmp(P); // p2: goto else patch(p1, pc); // test: - bgen(n->ntest, 0, p2); // if(!test) goto p2 + bgen(n->ntest, 0, -n->likely, p2); // if(!test) goto p2 genlist(n->nbody); // then p3 = gjmp(P); // goto done patch(p2, pc); // else: @@ -509,22 +511,24 @@ ret: void cgen_callmeth(Node *n, int proc) { + Node n2; Node *l; - // generate a rewrite for method call + // generate a rewrite in n2 for the method call // (p.f)(...) goes to (f)(p,...) l = n->left; if(l->op != ODOTMETH) fatal("cgen_callmeth: not dotmethod: %N"); - n->op = OCALLFUNC; - n->left = n->left->right; - n->left->type = l->type; + n2 = *n; + n2.op = OCALLFUNC; + n2.left = l->right; + n2.left->type = l->type; - if(n->left->op == ONAME) - n->left->class = PFUNC; - cgen_call(n, proc); + if(n2.left->op == ONAME) + n2.left->class = PFUNC; + cgen_call(&n2, proc); } /* @@ -631,6 +635,67 @@ cgen_discard(Node *nr) } /* + * clearslim generates code to zero a slim node. + */ +void +clearslim(Node *n) +{ + Node z; + Mpflt zero; + + memset(&z, 0, sizeof(z)); + z.op = OLITERAL; + z.type = n->type; + z.addable = 1; + + switch(simtype[n->type->etype]) { + case TCOMPLEX64: + case TCOMPLEX128: + z.val.u.cval = mal(sizeof(*z.val.u.cval)); + mpmovecflt(&z.val.u.cval->real, 0.0); + mpmovecflt(&z.val.u.cval->imag, 0.0); + break; + + case TFLOAT32: + case TFLOAT64: + mpmovecflt(&zero, 0.0); + z.val.ctype = CTFLT; + z.val.u.fval = &zero; + break; + + case TPTR32: + case TPTR64: + case TCHAN: + case TMAP: + z.val.ctype = CTNIL; + break; + + case TBOOL: + z.val.ctype = CTBOOL; + break; + + case TINT8: + case TINT16: + case TINT32: + case TINT64: + case TUINT8: + case TUINT16: + case TUINT32: + case TUINT64: + z.val.ctype = CTINT; + z.val.u.xval = mal(sizeof(*z.val.u.xval)); + mpmovecfix(z.val.u.xval, 0); + break; + + default: + fatal("clearslim called on type %T", n->type); + } + + ullmancalc(&z); + cgen(&z, n); +} + +/* * generate assignment: * nl = nr * nr == N means zero nl. @@ -638,9 +703,7 @@ cgen_discard(Node *nr) void cgen_as(Node *nl, Node *nr) { - Node nc; Type *tl; - int iszer; if(debug['g']) { dump("cgen_as", nl); @@ -655,7 +718,6 @@ cgen_as(Node *nl, Node *nr) return; } - iszer = 0; if(nr == N || isnil(nr)) { // externals and heaps should already be clear if(nr == N) { @@ -670,59 +732,10 @@ cgen_as(Node *nl, Node *nr) return; if(isfat(tl)) { clearfat(nl); - goto ret; - } - - /* invent a "zero" for the rhs */ - iszer = 1; - nr = &nc; - memset(nr, 0, sizeof(*nr)); - switch(simtype[tl->etype]) { - default: - fatal("cgen_as: tl %T", tl); - break; - - case TINT8: - case TUINT8: - case TINT16: - case TUINT16: - case TINT32: - case TUINT32: - case TINT64: - case TUINT64: - nr->val.u.xval = mal(sizeof(*nr->val.u.xval)); - mpmovecfix(nr->val.u.xval, 0); - nr->val.ctype = CTINT; - break; - - case TFLOAT32: - case TFLOAT64: - nr->val.u.fval = mal(sizeof(*nr->val.u.fval)); - mpmovecflt(nr->val.u.fval, 0.0); - nr->val.ctype = CTFLT; - break; - - case TBOOL: - nr->val.u.bval = 0; - nr->val.ctype = CTBOOL; - break; - - case TPTR32: - case TPTR64: - nr->val.ctype = CTNIL; - break; - - case TCOMPLEX64: - case TCOMPLEX128: - nr->val.u.cval = mal(sizeof(*nr->val.u.cval)); - mpmovecflt(&nr->val.u.cval->real, 0.0); - mpmovecflt(&nr->val.u.cval->imag, 0.0); - break; + return; } - nr->op = OLITERAL; - nr->type = tl; - nr->addable = 1; - ullmancalc(nr); + clearslim(nl); + return; } tl = nl->type; @@ -730,11 +743,88 @@ cgen_as(Node *nl, Node *nr) return; cgen(nr, nl); - if(iszer && nl->addable) - gused(nl); +} -ret: - ; +/* + * generate: + * res = iface{typ, data} + * n->left is typ + * n->right is data + */ +void +cgen_eface(Node *n, Node *res) +{ + /* + * the right node of an eface may contain function calls that uses res as an argument, + * so it's important that it is done first + */ + Node dst; + dst = *res; + dst.type = types[tptr]; + dst.xoffset += widthptr; + cgen(n->right, &dst); + dst.xoffset -= widthptr; + cgen(n->left, &dst); +} + +/* + * generate: + * res = s[lo, hi]; + * n->left is s + * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)]) + * caller (cgen) guarantees res is an addable ONAME. + */ +void +cgen_slice(Node *n, Node *res) +{ + Node src, dst, *cap, *len, *offs, *add; + + cap = n->list->n; + len = n->list->next->n; + offs = N; + if(n->list->next->next) + offs = n->list->next->next->n; + + // dst.len = hi [ - lo ] + dst = *res; + dst.xoffset += Array_nel; + dst.type = types[simtype[TUINT]]; + cgen(len, &dst); + + if(n->op != OSLICESTR) { + // dst.cap = cap [ - lo ] + dst = *res; + dst.xoffset += Array_cap; + dst.type = types[simtype[TUINT]]; + cgen(cap, &dst); + } + + // dst.array = src.array [ + lo *width ] + dst = *res; + dst.xoffset += Array_array; + dst.type = types[TUINTPTR]; + + if(n->op == OSLICEARR) { + if(!isptr[n->left->type->etype]) + fatal("slicearr is supposed to work on pointer: %+N\n", n); + checkref(n->left); + } + + if(isnil(n->left)) { + tempname(&src, n->left->type); + cgen(n->left, &src); + } else + src = *n->left; + src.xoffset += Array_array; + src.type = types[TUINTPTR]; + + if(offs == N) { + cgen(&src, &dst); + } else { + add = nod(OADD, &src, offs); + typecheck(&add, Erv); + cgen(add, &dst); + } } /* diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors index e29cfff5b..68a5e5af3 100644 --- a/src/cmd/gc/go.errors +++ b/src/cmd/gc/go.errors @@ -65,6 +65,9 @@ static struct { % loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';' "need trailing comma before newline in composite literal", + % loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';' + "need trailing comma before newline in composite literal", + % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME "nested func not allowed", diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 43bd68793..f86c152f2 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -137,6 +137,7 @@ typedef struct Label Label; struct Type { uchar etype; + uchar nointerface; uchar chan; uchar trecur; // to detect loops uchar printed; @@ -146,7 +147,7 @@ struct Type uchar copyany; uchar local; // created in this file uchar deferwidth; - uchar broke; + uchar broke; // broken type definition. uchar isddd; // TFIELD is ... argument uchar align; @@ -170,21 +171,25 @@ struct Type vlong argwid; // most nodes - Type* type; - vlong width; // offset in TFIELD, width in all others + Type* type; // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx + vlong width; // offset in TFIELD, width in all others // TFIELD - Type* down; // also used in TMAP + Type* down; // next struct field, also key type in TMAP + Type* outer; // outer struct Strlit* note; // literal string annotation // TARRAY - int32 bound; // negative is dynamic array + vlong bound; // negative is dynamic array int32 maplineno; // first use of TFORW as map key int32 embedlineno; // first use of TFORW as embedded type // for TFORW, where to copy the eventual value to NodeList *copyto; + + // for usefield + Node *lastfn; }; #define T ((Type*)0) @@ -215,7 +220,10 @@ enum EscHeap, EscScope, EscNone, + EscReturn, EscNever, + EscBits = 4, + EscMask = (1<<EscBits) - 1, }; struct Node @@ -233,16 +241,19 @@ struct Node NodeList* rlist; uchar op; + uchar nointerface; uchar ullman; // sethi/ullman number uchar addable; // type of addressability - 0 is not addressable uchar trecur; // to detect loops uchar etype; // op for OASOP, etype for OTYPE, exclam for export + uchar bounded; // bounds check unnecessary uchar class; // PPARAM, PAUTO, PEXTERN, etc uchar method; // OCALLMETH name uchar embedded; // ODCLFIELD embedded type uchar colas; // OAS resulting from := uchar diag; // already printed error about this uchar esc; // EscXXX + uchar noescape; // func arguments do not escape uchar funcdepth; uchar builtin; // built-in name, like len or close uchar walkdef; @@ -256,10 +267,10 @@ struct Node uchar implicit; uchar addrtaken; // address taken, even if not moved to heap uchar dupok; // duplicate definitions ok (for func) + schar likely; // likeliness of if statement // most nodes Type* type; - Type* realtype; // as determined by typecheck Node* orig; // original form, for printing, and tracking copies of ONAMEs // func @@ -279,7 +290,7 @@ struct Node Node* defn; // ONAME: initializing assignment; OLABEL: labeled statement Node* pack; // real package for import . names Node* curfn; // function for local variables - Type* paramfld; // TFIELD for this PPARAM + Type* paramfld; // TFIELD for this PPARAM; also for ODOT, curfn // ONAME func param with PHEAP Node* heapaddr; // temp holding heap address of param @@ -301,17 +312,19 @@ struct Node // Escape analysis. NodeList* escflowsrc; // flow(this, src) - int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes + NodeList* escretval; // on OCALLxxx, list of dummy return values + int escloopdepth; // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes Sym* sym; // various int32 vargen; // unique name for OTYPE/ONAME int32 lineno; int32 endlineno; vlong xoffset; - int32 stkdelta; // offset added by stack frame compaction phase. + vlong stkdelta; // offset added by stack frame compaction phase. int32 ostk; int32 iota; uint32 walkgen; + int32 esclevel; }; #define N ((Node*)0) @@ -345,6 +358,7 @@ enum SymExported = 1<<2, // already written out by export SymUniq = 1<<3, SymSiggen = 1<<4, + SymGcgen = 1<<5, }; struct Sym @@ -362,6 +376,7 @@ struct Sym Label* label; // corresponding label (ephemeral) int32 block; // blocknumber to catch redeclaration int32 lastlineno; // last declaration for diagnostic + Pkg* origpkg; // original package for . import }; #define S ((Sym*)0) @@ -374,6 +389,7 @@ struct Pkg Sym* pathsym; char* prefix; // escaped path for use in symbol table Pkg* link; + uchar imported; // export data of this package was parsed char exported; // import line written in export data char direct; // imported directly }; @@ -398,102 +414,171 @@ struct Hist }; #define H ((Hist*)0) +// Node ops. enum { OXXX, // names - ONAME, - ONONAME, - OTYPE, - OPACK, - OLITERAL, - - // exprs - OADD, OSUB, OOR, OXOR, OADDSTR, - OADDR, - OANDAND, - OAPPEND, - OARRAYBYTESTR, OARRAYRUNESTR, - OSTRARRAYBYTE, OSTRARRAYRUNE, - OAS, OAS2, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, - OASOP, - OBAD, - OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, - OCAP, - OCLOSE, - OCLOSURE, - OCMPIFACE, OCMPSTR, - OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, OPTRLIT, - OCONV, OCONVIFACE, OCONVNOP, - OCOPY, - ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE, - ODELETE, - ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT, - ODOTTYPE, - ODOTTYPE2, - OEQ, ONE, OLT, OLE, OGE, OGT, - OIND, - OINDEX, OINDEXMAP, - OKEY, OPARAM, - OLEN, - OMAKE, OMAKECHAN, OMAKEMAP, OMAKESLICE, - OHMUL, ORRC, OLRC, // high-mul and rotate-carry - OMUL, ODIV, OMOD, OLSH, ORSH, OAND, OANDNOT, - ONEW, - ONOT, OCOM, OPLUS, OMINUS, - OOROR, - OPANIC, OPRINT, OPRINTN, - OPAREN, - OSEND, - OSLICE, OSLICEARR, OSLICESTR, - ORECOVER, - ORECV, - ORUNESTR, - OSELRECV, - OSELRECV2, - OIOTA, - OREAL, OIMAG, OCOMPLEX, - - // stmts - OBLOCK, - OBREAK, - OCASE, OXCASE, - OCONTINUE, - ODEFER, - OEMPTY, - OFALL, OXFALL, - OFOR, - OGOTO, - OIF, - OLABEL, - OPROC, - ORANGE, - ORETURN, - OSELECT, - OSWITCH, - OTYPESW, // l = r.(type) + ONAME, // var, const or func name + ONONAME, // unnamed arg or return value: f(int, string) (int, error) { etc } + OTYPE, // type name + OPACK, // import + OLITERAL, // literal + + // expressions + OADD, // x + y + OSUB, // x - y + OOR, // x | y + OXOR, // x ^ y + OADDSTR, // s + "foo" + OADDR, // &x + OANDAND, // b0 && b1 + OAPPEND, // append + OARRAYBYTESTR, // string(bytes) + OARRAYRUNESTR, // string(runes) + OSTRARRAYBYTE, // []byte(s) + OSTRARRAYRUNE, // []rune(s) + OAS, // x = y or x := y + OAS2, // x, y, z = xx, yy, zz + OAS2FUNC, // x, y = f() + OAS2RECV, // x, ok = <-c + OAS2MAPR, // x, ok = m["foo"] + OAS2DOTTYPE, // x, ok = I.(int) + OASOP, // x += y + OCALL, // function call, method call or type conversion, possibly preceded by defer or go. + OCALLFUNC, // f() + OCALLMETH, // t.Method() + OCALLINTER, // err.Error() + OCAP, // cap + OCLOSE, // close + OCLOSURE, // f = func() { etc } + OCMPIFACE, // err1 == err2 + OCMPSTR, // s1 == s2 + OCOMPLIT, // composite literal, typechecking may convert to a more specific OXXXLIT. + OMAPLIT, // M{"foo":3, "bar":4} + OSTRUCTLIT, // T{x:3, y:4} + OARRAYLIT, // [2]int{3, 4} + OPTRLIT, // &T{x:3, y:4} + OCONV, // var i int; var u uint; i = int(u) + OCONVIFACE, // I(t) + OCONVNOP, // type Int int; var i int; var j Int; i = int(j) + OCOPY, // copy + ODCL, // var x int + ODCLFUNC, // func f() or func (r) f() + ODCLFIELD, // struct field, interface field, or func/method argument/return value. + ODCLCONST, // const pi = 3.14 + ODCLTYPE, // type Int int + ODELETE, // delete + ODOT, // t.x + ODOTPTR, // p.x that is implicitly (*p).x + ODOTMETH, // t.Method + ODOTINTER, // err.Error + OXDOT, // t.x, typechecking may convert to a more specific ODOTXXX. + ODOTTYPE, // e = err.(MyErr) + ODOTTYPE2, // e, ok = err.(MyErr) + OEQ, // x == y + ONE, // x != y + OLT, // x < y + OLE, // x <= y + OGE, // x >= y + OGT, // x > y + OIND, // *p + OINDEX, // a[i] + OINDEXMAP, // m[s] + OKEY, // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc. + OPARAM, // The on-stack copy of a parameter or return value that escapes. + OLEN, // len + OMAKE, // make, typechecking may convert to a more specfic OMAKEXXX. + OMAKECHAN, // make(chan int) + OMAKEMAP, // make(map[string]int) + OMAKESLICE, // make([]int, 0) + OMUL, // x * y + ODIV, // x / y + OMOD, // x % y + OLSH, // x << u + ORSH, // x >> u + OAND, // x & y + OANDNOT, // x &^ y + ONEW, // new + ONOT, // !b + OCOM, // ^x + OPLUS, // +x + OMINUS, // -y + OOROR, // b1 || b2 + OPANIC, // panic + OPRINT, // print + OPRINTN, // println + OPAREN, // (x) + OSEND, // c <- x + OSLICE, // v[1:2], typechecking may convert to a more specfic OSLICEXXX. + OSLICEARR, // a[1:2] + OSLICESTR, // s[1:2] + ORECOVER, // recover + ORECV, // <-c + ORUNESTR, // string(i) + OSELRECV, // case x = <-c: + OSELRECV2, // case x, ok = <-c: + OIOTA, // iota + OREAL, // real + OIMAG, // imag + OCOMPLEX, // complex + + // statements + OBLOCK, // block of code + OBREAK, // break + OCASE, // case, after being verified by swt.c's casebody. + OXCASE, // case, before verification. + OCONTINUE, // continue + ODEFER, // defer + OEMPTY, // no-op + OFALL, // fallthrough, after being verified by swt.c's casebody. + OXFALL, // fallthrough, before verification. + OFOR, // for + OGOTO, // goto + OIF, // if + OLABEL, // label: + OPROC, // go + ORANGE, // range + ORETURN, // return + OSELECT, // select + OSWITCH, // switch x + OTYPESW, // switch err.(type) // types - OTCHAN, - OTMAP, - OTSTRUCT, - OTINTER, - OTFUNC, - OTARRAY, - OTPAREN, + OTCHAN, // chan int + OTMAP, // map[string]int + OTSTRUCT, // struct{} + OTINTER, // interface{} + OTFUNC, // func() + OTARRAY, // []int, [8]int, [N]int or [...]int + OTPAREN, // (T) // misc - ODDD, - ODDDARG, - OINLCALL, // intermediary representation of an inlined call - OITAB, // itable word of interface value - - // for back ends - OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, + ODDD, // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}. + ODDDARG, // func f(args ...int), introduced by escape analysis. + OINLCALL, // intermediary representation of an inlined call. + OEFACE, // itable and data words of an empty-interface value. + OITAB, // itable word of an interface value. + OCLOSUREVAR, // variable reference at beginning of closure function + OCFUNC, // reference to c function pointer (not go func value) + + // arch-specific registers + OREGISTER, // a register, such as AX. + OINDREG, // offset plus indirect of a register, such as 8(SP). + + // 386/amd64-specific opcodes + OCMP, // compare: ACMP. + ODEC, // decrement: ADEC. + OINC, // increment: AINC. + OEXTEND, // extend: ACWD/ACDQ/ACQO. + OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both). + OLROT, // left rotate: AROL. + ORROTC, // right rotate-carry: ARCR. OEND, }; + enum { Txxx, // 0 @@ -539,6 +624,7 @@ enum NTYPE, }; + enum { CTxxx, @@ -562,18 +648,21 @@ enum Cboth = Crecv | Csend, }; +// declaration context enum { Pxxx, - PEXTERN, // declaration context - PAUTO, - PPARAM, - PPARAMOUT, - PPARAMREF, // param passed by reference - PFUNC, + PEXTERN, // global variable + PAUTO, // local variables + PPARAM, // input arguments + PPARAMOUT, // output results + PPARAMREF, // closure variable reference + PFUNC, // global function - PHEAP = 1<<7, + PDISCARD, // discard during parse of duplicate import + + PHEAP = 1<<7, // an extra bit to identify an escaped variable }; enum @@ -606,7 +695,6 @@ typedef struct Var Var; struct Var { vlong offset; - Sym* gotype; Node* node; int width; char name; @@ -756,7 +844,7 @@ EXTERN int safemode; EXTERN char namebuf[NSYMB]; EXTERN char lexbuf[NSYMB]; EXTERN char litbuf[NSYMB]; -EXTERN char debug[256]; +EXTERN int debug[256]; EXTERN Sym* hash[NHASH]; EXTERN Sym* importmyname; // my name for package EXTERN Pkg* localpkg; // package being compiled @@ -764,11 +852,15 @@ EXTERN Pkg* importpkg; // package being imported EXTERN Pkg* structpkg; // package that declared struct, during import EXTERN Pkg* builtinpkg; // fake package for builtins EXTERN Pkg* gostringpkg; // fake pkg for Go strings +EXTERN Pkg* itabpkg; // fake pkg for itab cache EXTERN Pkg* runtimepkg; // package runtime +EXTERN Pkg* racepkg; // package runtime/race EXTERN Pkg* stringpkg; // fake package for C strings -EXTERN Pkg* typepkg; // fake package for runtime type info +EXTERN Pkg* typepkg; // fake package for runtime type info (headers) +EXTERN Pkg* typelinkpkg; // fake package for runtime type info (data) EXTERN Pkg* weaktypepkg; // weak references to runtime type info EXTERN Pkg* unsafepkg; // package unsafe +EXTERN Pkg* trackpkg; // fake package for field tracking EXTERN Pkg* phash[128]; EXTERN int tptr; // either TPTR32 or TPTR64 extern char* runtimeimport; @@ -815,6 +907,7 @@ EXTERN NodeList* externdcl; EXTERN NodeList* closures; EXTERN NodeList* exportlist; EXTERN NodeList* importlist; // imported functions and methods with inlinable bodies +EXTERN NodeList* funcsyms; EXTERN int dclcontext; // PEXTERN/PAUTO EXTERN int incannedimport; EXTERN int statuniqgen; // name generator for static temps @@ -823,8 +916,8 @@ EXTERN int loophack; EXTERN int32 iota; EXTERN NodeList* lastconst; EXTERN Node* lasttype; -EXTERN int32 maxarg; -EXTERN int32 stksize; // stack size for current frame +EXTERN vlong maxarg; +EXTERN vlong stksize; // stack size for current frame EXTERN int32 blockgen; // max block number EXTERN int32 block; // current block number EXTERN int hasdefer; // flag that curfn has defer statetment @@ -832,12 +925,14 @@ EXTERN int hasdefer; // flag that curfn has defer statetment EXTERN Node* curfn; EXTERN int widthptr; +EXTERN int widthint; EXTERN Node* typesw; EXTERN Node* nblank; extern int thechar; extern char* thestring; +EXTERN int use_sse; EXTERN char* hunk; EXTERN int32 nhunk; @@ -846,6 +941,14 @@ EXTERN int32 thunk; EXTERN int funcdepth; EXTERN int typecheckok; EXTERN int compiling_runtime; +EXTERN int compiling_wrappers; +EXTERN int pure_go; +EXTERN int flag_race; +EXTERN int flag_largemodel; +EXTERN int noescape; + +EXTERN int nointerface; +EXTERN int fieldtrack_enabled; /* * y.tab.c @@ -860,7 +963,7 @@ void checkwidth(Type *t); void defercheckwidth(void); void dowidth(Type *t); void resumecheckwidth(void); -uint32 rnd(uint32 o, uint32 r); +vlong rnd(vlong o, vlong r); void typeinit(void); /* @@ -898,9 +1001,11 @@ void defaultlit(Node **np, Type *t); void defaultlit2(Node **lp, Node **rp, int force); void evconst(Node *n); int isconst(Node *n, int ct); +int isgoconst(Node *n); Node* nodcplxlit(Val r, Val i); Node* nodlit(Val v); long nonnegconst(Node *n); +int doesoverflow(Val v, Type *t); void overflow(Val v, Type *t); int smallintconst(Node *n); Val toint(Val v); @@ -910,7 +1015,7 @@ Mpflt* truncfltlit(Mpflt *oldv, Type *t); * cplx.c */ void complexadd(int op, Node *nl, Node *nr, Node *res); -void complexbool(int op, Node *nl, Node *nr, int true, Prog *to); +void complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to); void complexgen(Node *n, Node *res); void complexminus(Node *nl, Node *res); void complexmove(Node *f, Node *t); @@ -921,10 +1026,10 @@ void nodfconst(Node *n, Type *t, Mpflt* fval); /* * dcl.c */ -void addmethod(Sym *sf, Type *t, int local); +void addmethod(Sym *sf, Type *t, int local, int nointerface); void addvar(Node *n, Type *t, int ctxt); NodeList* checkarglist(NodeList *all, int input); -Node* colas(NodeList *left, NodeList *right); +Node* colas(NodeList *left, NodeList *right, int32 lno); void colasdefn(NodeList *left, Node *defn); NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); Node* dclname(Sym *s); @@ -954,6 +1059,7 @@ Node* typedcl0(Sym *s); Node* typedcl1(Node *n, Node *t, int local); Node* typenod(Type *t); NodeList* variter(NodeList *vl, Node *t, NodeList *el); +Sym* funcsym(Sym*); /* * esc.c @@ -987,6 +1093,8 @@ void dumplist(char *s, NodeList *l); void addrescapes(Node *n); void cgen_as(Node *nl, Node *nr); void cgen_callmeth(Node *n, int proc); +void cgen_eface(Node* n, Node* res); +void cgen_slice(Node* n, Node* res); void clearlabels(void); void checklabels(void); int dotoffset(Node *n, int *oary, Node **nn); @@ -1115,8 +1223,11 @@ void dumptypestructs(void); Type* methodfunc(Type *f, Type*); Node* typename(Type *t); Sym* typesym(Type *t); +Sym* typenamesym(Type *t); +Sym* tracksym(Type *t); Sym* typesymprefix(char *prefix, Type *t); int haspointers(Type *t); +void usefield(Node*); /* * select.c @@ -1214,6 +1325,7 @@ Node* safeexpr(Node *n, NodeList **init); void saveerrors(void); Node* cheapexpr(Node *n, NodeList **init); Node* localexpr(Node *n, Type *t, NodeList **init); +void saveorignode(Node *n); int32 setlineno(Node *n); void setmaxarg(Type *t); Type* shallow(Type *t); @@ -1274,6 +1386,7 @@ void walkexprlistsafe(NodeList *l, NodeList **init); void walkstmt(Node **np); void walkstmtlist(NodeList *l); Node* conv(Node*, Type*); +int candiscard(Node*); /* * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c @@ -1302,7 +1415,8 @@ EXTERN Node* nodfp; int anyregalloc(void); void betypeinit(void); -void bgen(Node *n, int true, Prog *to); +void bgen(Node *n, int true, int likely, Prog *to); +void checkref(Node*); void cgen(Node*, Node*); void cgen_asop(Node *n); void cgen_call(Node *n, int proc); @@ -1323,8 +1437,8 @@ void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); void genembedtramp(Type*, Type*, Sym*, int iface); -void ggloblnod(Node *nam, int32 width); -void ggloblsym(Sym *s, int32 width, int dupok); +void ggloblnod(Node *nam); +void ggloblsym(Sym *s, int32 width, int dupok, int rodata); Prog* gjmp(Prog*); void gused(Node*); int isfat(Type*); @@ -1364,3 +1478,8 @@ void zname(Biobuf *b, Sym *s, int t); #pragma varargck type "V" Val* #pragma varargck type "Y" char* #pragma varargck type "Z" Strlit* + +/* + * racewalk.c + */ +void racewalk(Node *fn); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index f95058721..794961e8e 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -37,8 +37,8 @@ static void fixlbrace(int); // |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' %token <val> LLITERAL -%token <i> LASOP -%token <sym> LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD +%token <i> LASOP LCOLAS +%token <sym> LBREAK LCASE LCHAN LCONST LCONTINUE LDDD %token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO %token <sym> LIF LIMPORT LINTERFACE LMAP LNAME %token <sym> LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH @@ -54,10 +54,10 @@ static void fixlbrace(int); %type <node> stmt ntype %type <node> arg_type %type <node> case caseblock -%type <node> compound_stmt dotname embed expr complitexpr +%type <node> compound_stmt dotname embed expr complitexpr bare_complitexpr %type <node> expr_or_type %type <node> fndcl hidden_fndcl fnliteral -%type <node> for_body for_header for_stmt if_header if_stmt else non_dcl_stmt +%type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt %type <node> interfacedcl keyval labelname name %type <node> name_or_type non_expr_type %type <node> new_name dcl_name oexpr typedclname @@ -70,7 +70,7 @@ static void fixlbrace(int); %type <list> xdcl fnbody fnres loop_body dcl_name_list %type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list -%type <list> oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list +%type <list> oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list %type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list %type <list> common_dcl constdcl constdcl1 constdcl_list typedcl_list @@ -405,6 +405,20 @@ simple_stmt: expr { $$ = $1; + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch($$->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + $$ = nod(OPAREN, $$, N); + $$->implicit = 1; + break; + } } | expr LASOP expr { @@ -437,7 +451,7 @@ simple_stmt: $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname. break; } - $$ = colas($1, $3); + $$ = colas($1, $3, $2); } | expr LINC { @@ -496,7 +510,7 @@ case: // done in casebody() markdcl(); $$ = nod(OXCASE, N, N); - $$->list = list1(colas($2, list1($4))); + $$->list = list1(colas($2, list1($4), $3)); } | LDEFAULT ':' { @@ -661,25 +675,56 @@ if_stmt: { $3->nbody = $5; } - else + elseif_list else { - popdcl(); + Node *n; + NodeList *nn; + $$ = $3; - if($7 != N) - $$->nelse = list1($7); + n = $3; + popdcl(); + for(nn = concat($7, $8); nn; nn = nn->next) { + if(nn->n->op == OIF) + popdcl(); + n->nelse = list1(nn->n); + n = nn->n; + } } -else: +elseif: + LELSE LIF { - $$ = N; + markdcl(); } -| LELSE if_stmt + if_header loop_body { - $$ = $2; + if($4->ntest == N) + yyerror("missing condition in if statement"); + $4->nbody = $5; + $$ = list1($4); + } + +elseif_list: + { + $$ = nil; + } +| elseif_list elseif + { + $$ = concat($1, $2); + } + +else: + { + $$ = nil; } | LELSE compound_stmt { - $$ = $2; + NodeList *node; + + node = mal(sizeof *node); + node->n = $2; + node->end = node; + $$ = node; } switch_stmt: @@ -902,7 +947,7 @@ pexpr_no_paren: $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); } | pseudocall -| convtype '(' expr ')' +| convtype '(' expr ocomma ')' { // conversion $$ = nod(OCALL, $1, N); @@ -943,6 +988,30 @@ keyval: $$ = nod(OKEY, $1, $3); } +bare_complitexpr: + expr + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + $$ = $1; + switch($$->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + $$ = nod(OPAREN, $$, N); + $$->implicit = 1; + } + } +| '{' start_complit braced_keyval_list '}' + { + $$ = $2; + $$->list = $3; + } + complitexpr: expr | '{' start_complit braced_keyval_list '}' @@ -966,6 +1035,7 @@ pexpr: case OPACK: case OTYPE: case OLITERAL: + case OTYPESW: $$ = nod(OPAREN, $$, N); } } @@ -1030,10 +1100,16 @@ sym: hidden_importsym: '@' LLITERAL '.' LNAME { + Pkg *p; + if($2.u.sval->len == 0) - $$ = pkglookup($4->name, importpkg); - else - $$ = pkglookup($4->name, mkpkg($2.u.sval)); + p = importpkg; + else { + if(isbadimport($2.u.sval)) + errorexit(); + p = mkpkg($2.u.sval); + } + $$ = pkglookup($4->name, p); } name: @@ -1201,8 +1277,11 @@ xfndcl: $$ = $2; if($$ == N) break; + if(noescape && $3 != nil) + yyerror("can only use //go:noescape with external func implementations"); $$->nbody = $3; $$->endlineno = lineno; + $$->noescape = noescape; funcbody($$); } @@ -1269,6 +1348,7 @@ fndcl: $$->nname = methodname1($$->shortname, rcvr->right); $$->nname->defn = $$; $$->nname->ntype = t; + $$->nname->nointerface = nointerface; declare($$->nname, PFUNC); funchdr($$); @@ -1287,8 +1367,10 @@ hidden_fndcl: importsym(s, ONAME); if(s->def != N && s->def->op == ONAME) { - if(eqtype(t, s->def->type)) + if(eqtype(t, s->def->type)) { + dclcontext = PDISCARD; // since we skip funchdr below break; + } yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t); } @@ -1304,7 +1386,8 @@ hidden_fndcl: $$->type = functype($2->n, $6, $8); checkwidth($$->type); - addmethod($4, $$->type, 0); + addmethod($4, $$->type, 0, nointerface); + nointerface = 0; funchdr($$); // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as @@ -1381,6 +1464,8 @@ xdcl_list: $$ = concat($1, $2); if(nsyntaxerrors == 0) testdclstack(); + nointerface = 0; + noescape = 0; } vardcl_list: @@ -1719,7 +1804,7 @@ keyval_list: { $$ = list1($1); } -| complitexpr +| bare_complitexpr { $$ = list1($1); } @@ -1727,7 +1812,7 @@ keyval_list: { $$ = list($1, $3); } -| keyval_list ',' complitexpr +| keyval_list ',' bare_complitexpr { $$ = list($1, $3); } @@ -1818,8 +1903,10 @@ hidden_import: } | LFUNC hidden_fndcl fnbody ';' { - if($2 == N) + if($2 == N) { + dclcontext = PEXTERN; // since we skip the funcbody below break; + } $2->inl = $3; @@ -1828,7 +1915,7 @@ hidden_import: if(debug['E']) { print("import [%Z] func %lN \n", importpkg->path, $2); - if(debug['l'] > 2 && $2->inl) + if(debug['m'] > 2 && $2->inl) print("inl body:%+H\n", $2->inl); } } @@ -2039,6 +2126,8 @@ hidden_constant: mpaddfixfix($2->val.u.xval, $4->val.u.xval, 0); break; } + $4->val.u.cval->real = $4->val.u.cval->imag; + mpmovecflt(&$4->val.u.cval->imag, 0.0); $$ = nodcplxlit($2->val, $4->val); } diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c index be402cc0c..918d37180 100644 --- a/src/cmd/gc/init.c +++ b/src/cmd/gc/init.c @@ -55,6 +55,10 @@ anyinit(NodeList *n) case ODCLTYPE: case OEMPTY: break; + case OAS: + if(isblank(l->n->left) && candiscard(l->n->right)) + break; + // fall through default: return 1; } diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c index b2b1faff7..1cc13a304 100644 --- a/src/cmd/gc/inl.c +++ b/src/cmd/gc/inl.c @@ -13,7 +13,7 @@ // 0: disabled // 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) // 2: early typechecking of all imported bodies -// 3: +// 3: allow variadic functions // 4: allow non-leaf functions , (breaks runtime.Caller) // 5: transitive inlining // @@ -39,9 +39,10 @@ static int ishairylist(NodeList *ll, int *budget); // Used by inlcalls static void inlnodelist(NodeList *l); static void inlnode(Node **np); -static void mkinlcall(Node **np, Node *fn); +static void mkinlcall(Node **np, Node *fn, int isddd); static Node* inlvar(Node *n); static Node* retvar(Type *n, int i); +static Node* argvar(Type *n, int i); static Node* newlabel(void); static Node* inlsubst(Node *n); static NodeList* inlsubstlist(NodeList *l); @@ -82,20 +83,18 @@ typecheckinl(Node *fn) Pkg *pkg; int save_safemode, lno; - if(fn->typecheck) - return; - lno = setlineno(fn); - if (debug['m']>2) - print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); - - // typecheckinl is only used for imported functions; + // typecheckinl is only for imported functions; // their bodies may refer to unsafe as long as the package // was marked safe during import (which was checked then). + // the ->inl of a local function has been typechecked before caninl copied it. pkg = fnpkg(fn); if (pkg == localpkg || pkg == nil) - fatal("typecheckinl on local function %lN", fn); + return; // typecheckinl on local function + + if (debug['m']>2) + print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); save_safemode = safemode; safemode = 0; @@ -103,7 +102,6 @@ typecheckinl(Node *fn) savefn = curfn; curfn = fn; typechecklist(fn->inl, Etop); - fn->typecheck = 1; curfn = savefn; safemode = save_safemode; @@ -111,10 +109,9 @@ typecheckinl(Node *fn) lineno = lno; } -// Caninl determines whether fn is inlineable. Currently that means: -// fn is exactly 1 statement, either a return or an assignment, and -// some temporary constraints marked TODO. If fn is inlineable, saves -// fn->nbody in fn->inl and substitutes it with a copy. +// Caninl determines whether fn is inlineable. +// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. +// fn and ->nbody will already have been typechecked. void caninl(Node *fn) { @@ -131,10 +128,14 @@ caninl(Node *fn) if(fn->nbody == nil) return; + if(fn->typecheck == 0) + fatal("caninl on non-typechecked function %N", fn); + // can't handle ... args yet - for(t=fn->type->type->down->down->type; t; t=t->down) - if(t->isddd) - return; + if(debug['l'] < 3) + for(t=fn->type->type->down->down->type; t; t=t->down) + if(t->isddd) + return; budget = 40; // allowed hairyness if(ishairylist(fn->nbody, &budget)) @@ -145,8 +146,6 @@ caninl(Node *fn) fn->nname->inl = fn->nbody; fn->nbody = inlcopylist(fn->nname->inl); - // nbody will have been typechecked, so we can set this: - fn->typecheck = 1; // hack, TODO, check for better way to link method nodes back to the thing with the ->inl // this is so export can find the body of a method @@ -195,19 +194,11 @@ ishairy(Node *n, int *budget) case OSWITCH: case OPROC: case ODEFER: - case ODCL: // declares locals as globals b/c of @"". qualification case ODCLTYPE: // can't print yet case ODCLCONST: // can't print yet return 1; break; - case OAS: - // x = <N> zero initializing assignments aren't representible in export yet. - // alternatively we may just skip them in printing and hope their DCL printed - // as a var will regenerate it - if(n->right == N) - return 1; - break; } (*budget)--; @@ -366,8 +357,8 @@ inlnode(Node **np) } case OCLOSURE: - // TODO do them here instead of in lex.c phase 6b, so escape analysis - // can avoid more heapmoves. + // TODO do them here (or earlier) instead of in walkcallclosure, + // so escape analysis can avoid more heapmoves. return; } @@ -464,10 +455,10 @@ inlnode(Node **np) if(debug['m']>3) print("%L:call to func %+N\n", n->lineno, n->left); if(n->left->inl) // normal case - mkinlcall(np, n->left); + mkinlcall(np, n->left, n->isddd); else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions if(n->left->sym->def) - mkinlcall(np, n->left->sym->def); + mkinlcall(np, n->left->sym->def, n->isddd); break; case OCALLMETH: @@ -480,7 +471,7 @@ inlnode(Node **np) if(n->left->type->nname == N) fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); - mkinlcall(np, n->left->type->nname); + mkinlcall(np, n->left->type->nname, n->isddd); break; } @@ -488,10 +479,10 @@ inlnode(Node **np) lineno = lno; } -static void mkinlcall1(Node **np, Node *fn); +static void mkinlcall1(Node **np, Node *fn, int isddd); static void -mkinlcall(Node **np, Node *fn) +mkinlcall(Node **np, Node *fn, int isddd) { int save_safemode; Pkg *pkg; @@ -503,7 +494,7 @@ mkinlcall(Node **np, Node *fn) pkg = fnpkg(fn); if(pkg != localpkg && pkg != nil) safemode = 0; - mkinlcall1(np, fn); + mkinlcall1(np, fn, isddd); safemode = save_safemode; } @@ -519,17 +510,25 @@ tinlvar(Type *t) return nblank; } +static int inlgen; + // if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. // On return ninit has the parameter assignments, the nbody is the // inlined function body and list, rlist contain the input, output // parameters. static void -mkinlcall1(Node **np, Node *fn) +mkinlcall1(Node **np, Node *fn, int isddd) { int i; + int chkargcount; Node *n, *call, *saveinlfn, *as, *m; NodeList *dcl, *ll, *ninit, *body; Type *t; + // For variadic fn. + int variadic, varargcount, multiret; + Node *vararg; + NodeList *varargs; + Type *varargtype, *vararrtype; if (fn->inl == nil) return; @@ -556,6 +555,8 @@ mkinlcall1(Node **np, Node *fn) ninit = n->ninit; +//dumplist("ninit pre", ninit); + if (fn->defn) // local function dcl = fn->defn->dcl; else // imported function @@ -567,7 +568,10 @@ mkinlcall1(Node **np, Node *fn) for(ll = dcl; ll; ll=ll->next) if(ll->n->op == ONAME) { ll->n->inlvar = inlvar(ll->n); - ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs + // Typecheck because inlvar is not necessarily a function parameter. + typecheck(&ll->n->inlvar, Erv); + if ((ll->n->class&~PHEAP) != PAUTO) + ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs if (ll->n->class == PPARAMOUT) // we rely on the order being correct here inlretvars = list(inlretvars, ll->n->inlvar); } @@ -580,49 +584,118 @@ mkinlcall1(Node **np, Node *fn) inlretvars = list(inlretvars, m); } - // assign arguments to the parameters' temp names - as = N; - if(fn->type->thistuple) { + // assign receiver. + if(fn->type->thistuple && n->left->op == ODOTMETH) { + // method call with a receiver. t = getthisx(fn->type)->type; if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) fatal("missing inlvar for %N\n", t->nname); - - if(n->left->op == ODOTMETH) { - if(!n->left->left) - fatal("method call without receiver: %+N", n); - if(t == T) - fatal("method call unknown receiver type: %+N", n); - as = nod(OAS, tinlvar(t), n->left->left); - } else { // non-method call to method - if(!n->list) - fatal("non-method call to method without first arg: %+N", n); - if(t != T) - as = nod(OAS, tinlvar(t), n->list->n); - } - + if(!n->left->left) + fatal("method call without receiver: %+N", n); + if(t == T) + fatal("method call unknown receiver type: %+N", n); + as = nod(OAS, tinlvar(t), n->left->left); if(as != N) { typecheck(&as, Etop); ninit = list(ninit, as); } } + // check if inlined function is variadic. + variadic = 0; + varargtype = T; + varargcount = 0; + for(t=fn->type->type->down->down->type; t; t=t->down) { + if(t->isddd) { + variadic = 1; + varargtype = t->type; + } + } + // but if argument is dotted too forget about variadicity. + if(variadic && isddd) + variadic = 0; + + // check if argument is actually a returned tuple from call. + multiret = 0; + if(n->list && !n->list->next) { + switch(n->list->n->op) { + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + if(n->list->n->left->type->outtuple > 1) + multiret = n->list->n->left->type->outtuple-1; + } + } + + if(variadic) { + varargcount = count(n->list) + multiret; + if(n->left->op != ODOTMETH) + varargcount -= fn->type->thistuple; + varargcount -= fn->type->intuple - 1; + } + + // assign arguments to the parameters' temp names as = nod(OAS2, N, N); - if(fn->type->intuple > 1 && n->list && !n->list->next) { - // TODO check that n->list->n is a call? - // TODO: non-method call to T.meth(f()) where f returns t, args... - as->rlist = n->list; - for(t = getinargx(fn->type)->type; t; t=t->down) - as->list = list(as->list, tinlvar(t)); - } else { - ll = n->list; - if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method - ll=ll->next; // was handled above in if(thistuple) + as->rlist = n->list; + ll = n->list; + + // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? + if(fn->type->thistuple && n->left->op != ODOTMETH) { + // non-method call to method + if(!n->list) + fatal("non-method call to method without first arg: %+N", n); + // append receiver inlvar to LHS. + t = getthisx(fn->type)->type; + if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) + fatal("missing inlvar for %N\n", t->nname); + if(t == T) + fatal("method call unknown receiver type: %+N", n); + as->list = list(as->list, tinlvar(t)); + ll = ll->next; // track argument count. + } - for(t = getinargx(fn->type)->type; t && ll; t=t->down) { + // append ordinary arguments to LHS. + chkargcount = n->list && n->list->next; + vararg = N; // the slice argument to a variadic call + varargs = nil; // the list of LHS names to put in vararg. + if(!chkargcount) { + // 0 or 1 expression on RHS. + for(t = getinargx(fn->type)->type; t; t=t->down) { + if(variadic && t->isddd) { + vararg = tinlvar(t); + for(i=0; i<varargcount && ll; i++) { + m = argvar(varargtype, i); + varargs = list(varargs, m); + as->list = list(as->list, m); + } + break; + } + as->list = list(as->list, tinlvar(t)); + } + } else { + // match arguments except final variadic (unless the call is dotted itself) + for(t = getinargx(fn->type)->type; t;) { + if(!ll) + break; + if(variadic && t->isddd) + break; as->list = list(as->list, tinlvar(t)); - as->rlist = list(as->rlist, ll->n); + t=t->down; ll=ll->next; } + // match varargcount arguments with variadic parameters. + if(variadic && t && t->isddd) { + vararg = tinlvar(t); + for(i=0; i<varargcount && ll; i++) { + m = argvar(varargtype, i); + varargs = list(varargs, m); + as->list = list(as->list, m); + ll=ll->next; + } + if(i==varargcount) + t=t->down; + } if(ll || t) fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list); } @@ -632,6 +705,25 @@ mkinlcall1(Node **np, Node *fn) ninit = list(ninit, as); } + // turn the variadic args into a slice. + if(variadic) { + as = nod(OAS, vararg, N); + if(!varargcount) { + as->right = nodnil(); + as->right->type = varargtype; + } else { + vararrtype = typ(TARRAY); + vararrtype->type = varargtype->type; + vararrtype->bound = varargcount; + + as->right = nod(OCOMPLIT, N, typenod(varargtype)); + as->right->list = varargs; + as->right = nod(OSLICE, as->right, nod(OKEY, N, N)); + } + typecheck(&as, Etop); + ninit = list(ninit, as); + } + // zero the outparams for(ll = inlretvars; ll; ll=ll->next) { as = nod(OAS, ll->n, N); @@ -640,12 +732,14 @@ mkinlcall1(Node **np, Node *fn) } inlretlabel = newlabel(); + inlgen++; body = inlsubstlist(fn->inl); body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return body = list(body, nod(OLABEL, inlretlabel, N)); typechecklist(body, Etop); +//dumplist("ninit post", ninit); call = nod(OINLCALL, N, N); call->ninit = ninit; @@ -655,6 +749,7 @@ mkinlcall1(Node **np, Node *fn) call->typecheck = 1; setlno(call, n->lineno); +//dumplist("call body", body); *np = call; @@ -705,7 +800,24 @@ retvar(Type *t, int i) { Node *n; - snprint(namebuf, sizeof(namebuf), ".r%d", i); + snprint(namebuf, sizeof(namebuf), "~r%d", i); + n = newname(lookup(namebuf)); + n->type = t->type; + n->class = PAUTO; + n->used = 1; + n->curfn = curfn; // the calling function, not the called one + curfn->dcl = list(curfn->dcl, n); + return n; +} + +// Synthesize a variable to store the inlined function's arguments +// when they come from a multiple return call. +static Node* +argvar(Type *t, int i) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), "~arg%d", i); n = newname(lookup(namebuf)); n->type = t->type; n->class = PAUTO; @@ -746,6 +858,7 @@ inlsubstlist(NodeList *ll) static Node* inlsubst(Node *n) { + char *p; Node *m, *as; NodeList *ll; @@ -788,6 +901,16 @@ inlsubst(Node *n) typecheck(&m, Etop); // dump("Return after substitution", m); return m; + + case OGOTO: + case OLABEL: + m = nod(OXXX, N, N); + *m = *n; + m->ninit = nil; + p = smprint("%s·%d", n->left->sym->name, inlgen); + m->left = newname(lookup(p)); + free(p); + return m; } diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 624dfb0b4..68ae6864d 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -30,6 +30,8 @@ static void addidir(char*); static int getlinepragma(void); static char *goos, *goarch, *goroot; +#define BOM 0xFEFF + // Compiler experiments. // These are controlled by the GOEXPERIMENT environment // variable recorded when the compiler is built. @@ -38,6 +40,7 @@ static struct { int *val; } exper[] = { // {"rune32", &rune32}, + {"fieldtrack", &fieldtrack_enabled}, {nil, nil}, }; @@ -96,7 +99,7 @@ yy_isdigit(int c) static int yy_isspace(int c) { - return c >= 0 && c <= 0xFF && isspace(c); + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static int @@ -130,42 +133,8 @@ enum void usage(void) { - print("gc: usage: %cg [flags] file.go...\n", thechar); - print("flags:\n"); - // -A allow use of "any" type, for bootstrapping - // -B disable bounds checking - // -E print imported declarations - // -K warn when lineno is zero - // -M print arguments to gmove - // -P print peephole diagnostics - // -R print optimizer diagnostics - // -g print code generation diagnostics - // -i print line history - // -j print variables to be initialized at runtime - // -r print generated helper functions - // -s print redundant types in composite literals - // -v print more information with -P or -R - // -y print declarations in cannedimports (used with -d) - // -% print non-static initializers - // -+ indicate that the runtime is being compiled - print(" -D PATH interpret local imports relative to this import path\n"); - print(" -I DIR search for packages in DIR\n"); - print(" -L show full path in file:line prints\n"); - print(" -N disable optimizations\n"); - print(" -S print the assembly language\n"); - print(" -V print the compiler version\n"); - print(" -W print the parse tree after typing\n"); - print(" -d print declarations\n"); - print(" -e no limit on number of errors printed\n"); - print(" -f print stack frame structure\n"); - print(" -h panic on an error\n"); - print(" -l disable inlining\n"); - print(" -m print optimization decisions\n"); - print(" -o file specify output file\n"); - print(" -p assumed import path for this code\n"); - print(" -u disable package unsafe\n"); - print(" -w print type checking details\n"); - print(" -x print lex tokens\n"); + print("usage: %cg [options] file.go...\n", thechar); + flagprint(1); exits("usage"); } @@ -183,11 +152,23 @@ fault(int s) fatal("fault"); } +void +doversion(void) +{ + char *p; + + p = expstring(); + if(strcmp(p, "X:none") == 0) + p = ""; + print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); + exits(0); +} + int main(int argc, char *argv[]) { - int i, c; - NodeList *l, *batch; + int i; + NodeList *l; char *p; #ifdef SIGBUS @@ -197,25 +178,41 @@ main(int argc, char *argv[]) localpkg = mkpkg(strlit("")); localpkg->prefix = "\"\""; - + + // pseudo-package, for scoping builtinpkg = mkpkg(strlit("go.builtin")); + // pseudo-package, accessed by import "unsafe" + unsafepkg = mkpkg(strlit("unsafe")); + unsafepkg->name = "unsafe"; + + // real package, referred to by generated runtime calls + runtimepkg = mkpkg(strlit("runtime")); + runtimepkg->name = "runtime"; + + // pseudo-packages used in symbol tables gostringpkg = mkpkg(strlit("go.string")); gostringpkg->name = "go.string"; gostringpkg->prefix = "go.string"; // not go%2estring - runtimepkg = mkpkg(strlit("runtime")); - runtimepkg->name = "runtime"; + itabpkg = mkpkg(strlit("go.itab")); + itabpkg->name = "go.itab"; + itabpkg->prefix = "go.itab"; // not go%2eitab - typepkg = mkpkg(strlit("type")); - typepkg->name = "type"; + weaktypepkg = mkpkg(strlit("go.weak.type")); + weaktypepkg->name = "go.weak.type"; + weaktypepkg->prefix = "go.weak.type"; // not go%2eweak%2etype + + typelinkpkg = mkpkg(strlit("go.typelink")); + typelinkpkg->name = "go.typelink"; + typelinkpkg->prefix = "go.typelink"; // not go%2etypelink - weaktypepkg = mkpkg(strlit("weak.type")); - weaktypepkg->name = "weak.type"; - weaktypepkg->prefix = "weak.type"; // not weak%2etype + trackpkg = mkpkg(strlit("go.track")); + trackpkg->name = "go.track"; + trackpkg->prefix = "go.track"; // not go%2etrack - unsafepkg = mkpkg(strlit("unsafe")); - unsafepkg->name = "unsafe"; + typepkg = mkpkg(strlit("type")); + typepkg->name = "type"; goroot = getgoroot(); goos = getgoos(); @@ -224,41 +221,55 @@ main(int argc, char *argv[]) setexp(); outfile = nil; - ARGBEGIN { - default: - c = ARGC(); - if(c >= 0 && c < sizeof(debug)) - debug[c]++; - break; - - case 'o': - outfile = EARGF(usage()); - break; - - case 'p': - myimportpath = EARGF(usage()); - break; + flagcount("+", "compiling runtime", &compiling_runtime); + flagcount("%", "debug non-static initializers", &debug['%']); + flagcount("A", "for bootstrapping, allow 'any' type", &debug['A']); + flagcount("B", "disable bounds checking", &debug['B']); + flagstr("D", "path: set relative path for local imports", &localimport); + flagcount("E", "debug symbol export", &debug['E']); + flagfn1("I", "dir: add dir to import search path", addidir); + flagcount("K", "debug missing line numbers", &debug['K']); + flagcount("L", "use full (long) path in error messages", &debug['L']); + flagcount("M", "debug move generation", &debug['M']); + flagcount("N", "disable optimizations", &debug['N']); + flagcount("P", "debug peephole optimizer", &debug['P']); + flagcount("R", "debug register optimizer", &debug['R']); + flagcount("S", "print assembly listing", &debug['S']); + flagfn0("V", "print compiler version", doversion); + flagcount("W", "debug parse tree after type checking", &debug['W']); + flagcount("complete", "compiling complete package (no C or assembly)", &pure_go); + flagcount("d", "debug declarations", &debug['d']); + flagcount("e", "no limit on number of errors reported", &debug['e']); + flagcount("f", "debug stack frames", &debug['f']); + flagcount("g", "debug code generation", &debug['g']); + flagcount("h", "halt on error", &debug['h']); + flagcount("i", "debug line number stack", &debug['i']); + flagcount("j", "debug runtime-initialized variables", &debug['j']); + flagcount("l", "disable inlining", &debug['l']); + flagcount("m", "print optimization decisions", &debug['m']); + flagstr("o", "obj: set output file", &outfile); + flagstr("p", "path: set expected package import path", &myimportpath); + flagcount("r", "debug generated wrappers", &debug['r']); + flagcount("race", "enable race detector", &flag_race); + flagcount("s", "warn about composite literals that can be simplified", &debug['s']); + flagcount("u", "reject unsafe code", &safemode); + flagcount("v", "increase debug verbosity", &debug['v']); + flagcount("w", "debug type checking", &debug['w']); + flagcount("x", "debug lexer", &debug['x']); + flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']); + if(thechar == '6') + flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); + + flagparse(&argc, &argv, usage); - case 'u': - safemode = 1; - break; + if(argc < 1) + usage(); - case 'D': - localimport = EARGF(usage()); - break; + if(flag_race) { + racepkg = mkpkg(strlit("runtime/race")); + racepkg->name = "race"; + } - case 'I': - addidir(EARGF(usage())); - break; - - case 'V': - p = expstring(); - if(strcmp(p, "X:none") == 0) - p = ""; - print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p); - exits(0); - } ARGEND - // enable inlining. for now: // default: inlining on. (debug['l'] == 1) // -l: inlining off (debug['l'] == 0) @@ -266,11 +277,15 @@ main(int argc, char *argv[]) if(debug['l'] <= 1) debug['l'] = 1 - debug['l']; - if(argc < 1) - usage(); - - // special flag to detect compilation of package runtime - compiling_runtime = debug['+']; + if(thechar == '8') { + p = getgo386(); + if(strcmp(p, "387") == 0) + use_sse = 0; + else if(strcmp(p, "sse2") == 0) + use_sse = 1; + else + sysfatal("unsupported setting GO386=%s", p); + } pathname = mal(1000); if(getwd(pathname, 999) == 0) @@ -315,6 +330,10 @@ main(int argc, char *argv[]) curio.peekc1 = 0; curio.nlsemi = 0; + // Skip initial BOM if present. + if(Bgetrune(curio.bin) != BOM) + Bungetrune(curio.bin); + block = 1; iota = -1000000; @@ -335,6 +354,7 @@ main(int argc, char *argv[]) frame(1); // 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. @@ -367,7 +387,7 @@ main(int argc, char *argv[]) errorexit(); // Phase 4: Inlining - if (debug['l'] > 1) { + if(debug['l'] > 1) { // Typecheck imported function bodies if debug['l'] > 1, // otherwise lazily when used or re-exported. for(l=importlist; l; l=l->next) @@ -380,7 +400,7 @@ main(int argc, char *argv[]) errorexit(); } - if (debug['l']) { + if(debug['l']) { // Find functions that can be inlined and clone them before walk expands them. for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) @@ -392,7 +412,7 @@ main(int argc, char *argv[]) inlcalls(l->n); } - // Phase 5: escape analysis. + // Phase 5: Escape analysis. if(!debug['N']) escapes(xtop); @@ -404,21 +424,7 @@ main(int argc, char *argv[]) if(nsavederrors+nerrors == 0) fninit(xtop); - // Phase 6b: Compile all closures. - // Can generate more closures, so run in batches. - while(closures) { - batch = closures; - closures = nil; - if(debug['l']) - for(l=batch; l; l=l->next) - inlcalls(l->n); - if(!debug['N']) - escapes(batch); - for(l=batch; l; l=l->next) - funccompile(l->n, 1); - } - - // Phase 7: check external declarations. + // Phase 7: Check external declarations. for(l=externdcl; l; l=l->next) if(l->n->op == ONAME) typecheck(&l->n, Erv); @@ -483,7 +489,7 @@ skiptopkgdef(Biobuf *b) if(memcmp(p, "!<arch>\n", 8) != 0) return 0; /* symbol table is first; skip it */ - sz = arsize(b, "__.SYMDEF"); + sz = arsize(b, "__.GOSYMDEF"); if(sz < 0) return 0; Bseek(b, sz, 1); @@ -533,7 +539,7 @@ static int findpkg(Strlit *name) { Idir *p; - char *q; + char *q, *race; if(islocalname(name)) { if(safemode) @@ -571,10 +577,13 @@ findpkg(Strlit *name) return 1; } if(goroot != nil) { - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.a", goroot, goos, goarch, name); + race = ""; + if(flag_race) + race = "_race"; + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.a", goroot, goos, goarch, race, name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.%c", goroot, goos, goarch, name, thechar); + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s/%Z.%c", goroot, goos, goarch, race, name, thechar); if(access(namebuf, 0) >= 0) return 1; } @@ -659,6 +668,11 @@ importfile(Val *f, int line) strcat(cleanbuf, path->s); cleanname(cleanbuf); path = strlit(cleanbuf); + + if(isbadimport(path)) { + fakeimport(); + return; + } } if(!findpkg(path)) { @@ -667,6 +681,16 @@ importfile(Val *f, int line) } importpkg = mkpkg(path); + // If we already saw that package, feed a dummy statement + // to the lexer to avoid parsing export data twice. + if(importpkg->imported) { + file = strdup(namebuf); + p = smprint("package %s\n$$\n", importpkg->name); + cannedimports(file, p); + return; + } + importpkg->imported = 1; + imp = Bopen(namebuf, OREAD); if(imp == nil) { yyerror("can't open import: \"%Z\": %r", f->u.sval); @@ -767,12 +791,8 @@ static int isfrog(int c) { // complain about possibly invisible control characters - if(c < 0) - return 1; if(c < ' ') { - if(c == '\n' || c== '\r' || c == '\t') // good white space - return 0; - return 1; + return !yy_isspace(c); // exclude good white space } if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. return 1; @@ -982,6 +1002,7 @@ l0: c1 = getc(); if(c1 == '=') { c = LCOLAS; + yylval.i = lexlineno; goto lx; } break; @@ -1145,6 +1166,11 @@ l0: case '[': if(loophack || lstk != nil) { h = malloc(sizeof *h); + if(h == nil) { + flusherrors(); + yyerror("out of memory"); + errorexit(); + } h->v = loophack; h->next = lstk; lstk = h; @@ -1208,7 +1234,7 @@ talph: rune = getr(); // 0xb7 · is used for internal names if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) - yyerror("invalid identifier character 0x%ux", rune); + yyerror("invalid identifier character U+%04x", rune); cp += runetochar(cp, &rune); } else if(!yy_isalnum(c) && c != '_') break; @@ -1270,12 +1296,14 @@ tnum: continue; if(cp == lexbuf+2) yyerror("malformed hex constant"); + if(c == 'p') + goto caseep; goto ncu; } } if(c == 'p') // 0p begins floating point zero - goto casep; + goto caseep; c1 = 0; for(;;) { @@ -1293,7 +1321,7 @@ tnum: if(c == '.') goto casedot; if(c == 'e' || c == 'E') - goto casee; + goto caseep; if(c == 'i') goto casei; if(c1) @@ -1303,10 +1331,8 @@ tnum: dc: if(c == '.') goto casedot; - if(c == 'e' || c == 'E') - goto casee; - if(c == 'p' || c == 'P') - goto casep; + if(c == 'e' || c == 'E' || c == 'p' || c == 'P') + goto caseep; if(c == 'i') goto casei; @@ -1342,29 +1368,8 @@ casedot: if(c != 'e' && c != 'E') goto caseout; -casee: - *cp++ = 'e'; - c = getc(); - if(c == '+' || c == '-') { - *cp++ = c; - c = getc(); - } - if(!yy_isdigit(c)) - yyerror("malformed fp constant exponent"); - while(yy_isdigit(c)) { - if(cp+10 >= ep) { - yyerror("identifier too long"); - errorexit(); - } - *cp++ = c; - c = getc(); - } - if(c == 'i') - goto casei; - goto caseout; - -casep: - *cp++ = 'p'; +caseep: + *cp++ = c; c = getc(); if(c == '+' || c == '-') { *cp++ = c; @@ -1430,7 +1435,12 @@ getlinepragma(void) char *cp, *ep, *linep; Hist *h; - for(i=0; i<5; i++) { + c = getr(); + if(c == 'g') + goto go; + if(c != 'l') + goto out; + for(i=1; i<5; i++) { c = getr(); if(c != "line "[i]) goto out; @@ -1478,7 +1488,35 @@ getlinepragma(void) } } linehist(strdup(lexbuf), n, 0); + goto out; + +go: + cp = lexbuf; + ep = lexbuf+sizeof(lexbuf)-5; + *cp++ = 'g'; // already read + for(;;) { + c = getr(); + if(c == EOF || c >= Runeself) + goto out; + if(c == '\n') + break; + if(cp < ep) + *cp++ = c; + } + *cp = 0; + ep = strchr(lexbuf, ' '); + if(ep != nil) + *ep = 0; + if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) { + nointerface = 1; + goto out; + } + if(strcmp(lexbuf, "go:noescape") == 0) { + noescape = 1; + goto out; + } + out: return c; } @@ -1524,24 +1562,35 @@ yylex(void) static int getc(void) { - int c; + int c, c1, c2; c = curio.peekc; if(c != 0) { curio.peekc = curio.peekc1; curio.peekc1 = 0; - if(c == '\n' && pushedio.bin == nil) - lexlineno++; - return c; + goto check; } if(curio.bin == nil) { c = *curio.cp & 0xff; if(c != 0) curio.cp++; - } else + } else { + loop: c = Bgetc(curio.bin); + if(c == 0xef) { + c1 = Bgetc(curio.bin); + c2 = Bgetc(curio.bin); + if(c1 == 0xbb && c2 == 0xbf) { + yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file"); + goto loop; + } + Bungetc(curio.bin); + Bungetc(curio.bin); + } + } +check: switch(c) { case 0: if(curio.bin != nil) { @@ -1820,16 +1869,16 @@ lexinit(void) if(etype != Txxx) { if(etype < 0 || etype >= nelem(types)) fatal("lexinit: %s bad etype", s->name); + s1 = pkglookup(syms[i].name, builtinpkg); t = types[etype]; if(t == T) { t = typ(etype); - t->sym = s; + t->sym = s1; if(etype != TANY && etype != TSTRING) dowidth(t); types[etype] = t; } - s1 = pkglookup(syms[i].name, builtinpkg); s1->lexical = LNAME; s1->def = typenod(t); continue; @@ -1959,8 +2008,10 @@ lexfini(void) s->lexical = lex; etype = syms[i].etype; - if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) + if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) { s->def = typenod(types[etype]); + s->origpkg = builtinpkg; + } etype = syms[i].op; if(etype != OXXX && s->def == N) { @@ -1968,54 +2019,68 @@ lexfini(void) s->def->sym = s; s->def->etype = etype; s->def->builtin = 1; + s->origpkg = builtinpkg; } } + // backend-specific builtin types (e.g. int). for(i=0; typedefs[i].name; i++) { s = lookup(typedefs[i].name); - if(s->def == N) + if(s->def == N) { s->def = typenod(types[typedefs[i].etype]); + s->origpkg = builtinpkg; + } } // there's only so much table-driven we can handle. // these are special cases. s = lookup("byte"); - if(s->def == N) + if(s->def == N) { s->def = typenod(bytetype); - + s->origpkg = builtinpkg; + } + s = lookup("error"); - if(s->def == N) + if(s->def == N) { s->def = typenod(errortype); + s->origpkg = builtinpkg; + } s = lookup("rune"); - if(s->def == N) + if(s->def == N) { s->def = typenod(runetype); + s->origpkg = builtinpkg; + } s = lookup("nil"); if(s->def == N) { v.ctype = CTNIL; s->def = nodlit(v); s->def->sym = s; + s->origpkg = builtinpkg; } - + s = lookup("iota"); if(s->def == N) { s->def = nod(OIOTA, N, N); s->def->sym = s; + s->origpkg = builtinpkg; } s = lookup("true"); if(s->def == N) { s->def = nodbool(1); s->def->sym = s; + s->origpkg = builtinpkg; } s = lookup("false"); if(s->def == N) { s->def = nodbool(0); s->def->sym = s; + s->origpkg = builtinpkg; } - + nodfp = nod(ONAME, N, N); nodfp->type = types[TINT32]; nodfp->xoffset = 0; @@ -2179,7 +2244,7 @@ mkpackage(char* pkgname) { Sym *s; int32 h; - char *p; + char *p, *q; if(localpkg->name == nil) { if(strcmp(pkgname, "_") == 0) @@ -2219,6 +2284,11 @@ mkpackage(char* pkgname) if(outfile == nil) { p = strrchr(infile, '/'); + if(windows) { + q = strrchr(infile, '\\'); + if(q > p) + p = q; + } if(p == nil) p = infile; else diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index 33fa90e2e..e25044a8b 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -43,10 +43,10 @@ mpcmpfixfix(Mpint *a, Mpint *b) int mpcmpfixc(Mpint *b, vlong c) { - Mpint a; + Mpint c1; - mpmovecfix(&a, c); - return mpcmpfixfix(&a, b); + mpmovecfix(&c1, c); + return mpcmpfixfix(b, &c1); } int @@ -232,16 +232,78 @@ mppow10flt(Mpflt *a, int p) mpmulcflt(a, 10); } +static void +mphextofix(Mpint *a, char *s, int n) +{ + char *hexdigitp, *end, c; + long d; + int bit; + + while(*s == '0') { + s++; + n--; + } + + // overflow + if(4*n > Mpscale*Mpprec) { + a->ovf = 1; + return; + } + + end = s+n-1; + for(hexdigitp=end; hexdigitp>=s; hexdigitp--) { + c = *hexdigitp; + if(c >= '0' && c <= '9') + d = c-'0'; + else if(c >= 'A' && c <= 'F') + d = c-'A'+10; + else + d = c-'a'+10; + + bit = 4*(end - hexdigitp); + while(d > 0) { + if(d & 1) + a->a[bit/Mpscale] |= (long)1 << (bit%Mpscale); + bit++; + d = d >> 1; + } + } +} + // // floating point input -// required syntax is [+-]d*[.]d*[e[+-]d*] +// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] // void mpatoflt(Mpflt *a, char *as) { Mpflt b; - int dp, c, f, ef, ex, eb; - char *s; + int dp, c, f, ef, ex, eb, base; + char *s, *start; + + while(*as == ' ' || *as == '\t') + as++; + + /* determine base */ + s = as; + base = -1; + while(base == -1) { + switch(*s++) { + case '-': + case '+': + break; + + case '0': + if(*s == 'x') + base = 16; + else + base = 10; + break; + + default: + base = 10; + } + } s = as; dp = 0; /* digits after decimal point */ @@ -250,6 +312,37 @@ mpatoflt(Mpflt *a, char *as) eb = 0; /* binary point */ mpmovecflt(a, 0.0); + if(base == 16) { + start = nil; + for(;;) { + c = *s; + if(c == '-') { + f = 1; + s++; + } + else if(c == '+') { + s++; + } + else if(c == '0' && s[1] == 'x') { + s += 2; + start = s; + } + else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + s++; + } + else { + break; + } + } + if(start == nil) + goto bad; + + mphextofix(&a->val, start, s-start); + if(a->val.ovf) + goto bad; + a->exp = 0; + mpnorm(a); + } for(;;) { switch(c = *s++) { default: @@ -259,11 +352,13 @@ mpatoflt(Mpflt *a, char *as) f = 1; case ' ': - case '\t': - case '+': + case '\t': + case '+': continue; case '.': + if(base == 16) + goto bad; dp = 1; continue; @@ -355,7 +450,7 @@ void mpatofix(Mpint *a, char *as) { int c, f; - char *s; + char *s, *s0; s = as; f = 0; @@ -402,28 +497,19 @@ oct: goto out; hex: - c = *s++; + s0 = s; + c = *s; while(c) { - if(c >= '0' && c <= '9') { - mpmulcfix(a, 16); - mpaddcfix(a, c-'0'); - c = *s++; - continue; - } - if(c >= 'a' && c <= 'f') { - mpmulcfix(a, 16); - mpaddcfix(a, c+10-'a'); - c = *s++; - continue; - } - if(c >= 'A' && c <= 'F') { - mpmulcfix(a, 16); - mpaddcfix(a, c+10-'A'); - c = *s++; + if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + s++; + c = *s; continue; } goto bad; } + mphextofix(a, s0, s-s0); + if(a->ovf) + goto bad; out: if(f) @@ -439,8 +525,8 @@ int Bconv(Fmt *fp) { char buf[500], *p; - Mpint *xval, q, r, ten; - int f; + Mpint *xval, q, r, ten, sixteen; + int f, digit; xval = va_arg(fp->args, Mpint*); mpmovefixfix(&q, xval); @@ -449,15 +535,33 @@ Bconv(Fmt *fp) f = 1; mpnegfix(&q); } - mpmovecfix(&ten, 10); p = &buf[sizeof(buf)]; *--p = 0; - for(;;) { - mpdivmodfixfix(&q, &r, &q, &ten); - *--p = mpgetfix(&r) + '0'; - if(mptestfix(&q) <= 0) - break; + if(fp->flags & FmtSharp) { + // Hexadecimal + mpmovecfix(&sixteen, 16); + for(;;) { + mpdivmodfixfix(&q, &r, &q, &sixteen); + digit = mpgetfix(&r); + if(digit < 10) + *--p = digit + '0'; + else + *--p = digit - 10 + 'A'; + if(mptestfix(&q) <= 0) + break; + } + *--p = 'x'; + *--p = '0'; + } else { + // Decimal + mpmovecfix(&ten, 10); + for(;;) { + mpdivmodfixfix(&q, &r, &q, &ten); + *--p = mpgetfix(&r) + '0'; + if(mptestfix(&q) <= 0) + break; + } } if(f) *--p = '-'; @@ -501,10 +605,10 @@ Fconv(Fmt *fp) } if(fv.exp >= 0) { - snprint(buf, sizeof(buf), "%Bp+%d", &fv.val, fv.exp); + snprint(buf, sizeof(buf), "%#Bp+%d", &fv.val, fv.exp); goto out; } - snprint(buf, sizeof(buf), "%Bp-%d", &fv.val, -fv.exp); + snprint(buf, sizeof(buf), "%#Bp-%d", &fv.val, -fv.exp); out: return fmtstrcpy(fp, buf); diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index e45b4e0d4..b87d35b7b 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -59,7 +59,13 @@ dumpglobls(void) continue; dowidth(n->type); - ggloblnod(n, n->type->width); + ggloblnod(n); + } + + for(l=funcsyms; l; l=l->next) { + n = l->n; + dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0); + ggloblsym(n->sym, widthptr, 1, 1); } } @@ -302,8 +308,8 @@ stringsym(char *s, int len) off = 0; // string header - off = dsymptr(sym, off, sym, widthptr+4); - off = duint32(sym, off, len); + off = dsymptr(sym, off, sym, widthptr+widthint); + off = duintxx(sym, off, len, widthint); // string data for(n=0; n<len; n+=m) { @@ -314,7 +320,7 @@ stringsym(char *s, int len) } off = duint8(sym, off, 0); // terminating NUL for runtime off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment - ggloblsym(sym, off, 1); + ggloblsym(sym, off, 1, 1); return sym; } diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c index 2cab5fb95..499a4e746 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -276,11 +276,11 @@ orderstmt(Node *n, NodeList **out) case OSELRECV2: orderexprinplace(&r->left); orderexprinplace(&r->ntest); - orderexpr(&r->right->left, out); + orderexpr(&r->right->left, &l->n->ninit); break; case OSEND: - orderexpr(&r->left, out); - orderexpr(&r->right, out); + orderexpr(&r->left, &l->n->ninit); + orderexpr(&r->right, &l->n->ninit); break; } } diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index f2b75d61b..df8903baf 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -14,11 +14,12 @@ compile(Node *fn) { Plist *pl; Node nod1, *n; - Prog *ptxt; + Prog *plocals, *ptxt, *p, *p1; int32 lno; Type *t; Iter save; vlong oldstksize; + NodeList *l; if(newproc == N) { newproc = sysfunc("newproc"); @@ -29,16 +30,19 @@ compile(Node *fn) throwreturn = sysfunc("throwreturn"); } - if(fn->nbody == nil) - return; + lno = setlineno(fn); + + if(fn->nbody == nil) { + if(pure_go || memcmp(fn->nname->sym->name, "init·", 6) == 0) + yyerror("missing function body", fn); + goto ret; + } saveerrors(); // set up domain for labels clearlabels(); - lno = setlineno(fn); - curfn = fn; dowidth(curfn->type); @@ -63,6 +67,10 @@ compile(Node *fn) walk(curfn); if(nerrors != 0) goto ret; + if(flag_race) + racewalk(curfn); + if(nerrors != 0) + goto ret; continpc = P; breakpc = P; @@ -76,15 +84,34 @@ compile(Node *fn) ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); if(fn->dupok) ptxt->TEXTFLAG = DUPOK; - afunclit(&ptxt->from); + afunclit(&ptxt->from, curfn->nname); ginit(); + + plocals = gins(ALOCALS, N, N); + + for(t=curfn->paramfld; t; t=t->down) + gtrack(tracksym(t->type)); + + for(l=fn->dcl; l; l=l->next) { + n = l->n; + if(n->op != ONAME) // might be OTYPE or OLITERAL + continue; + switch(n->class) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + nodconst(&nod1, types[TUINTPTR], l->n->type->width); + p = gins(ATYPE, l->n, &nod1); + p->from.gotype = ngotype(l->n); + break; + } + } + genlist(curfn->enter); retpc = nil; if(hasdefer || curfn->exit) { - Prog *p1; - p1 = gjmp(nil); retpc = gjmp(nil); patch(p1, pc); @@ -111,6 +138,7 @@ compile(Node *fn) gclean(); if(nerrors != 0) goto ret; + pc->as = ARET; // overwrite AEND pc->lineno = lineno; @@ -120,6 +148,10 @@ compile(Node *fn) oldstksize = stksize; allocauto(ptxt); + + plocals->to.type = D_CONST; + plocals->to.offset = stksize; + if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); @@ -177,8 +209,10 @@ allocauto(Prog* ptxt) ll = curfn->dcl; n = ll->n; if (n->class == PAUTO && n->op == ONAME && !n->used) { + // No locals used at all curfn->dcl = nil; stksize = 0; + fixautoused(ptxt); return; } diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c new file mode 100644 index 000000000..bae98ec1b --- /dev/null +++ b/src/cmd/gc/racewalk.c @@ -0,0 +1,579 @@ +// 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. + +// The racewalk pass modifies the code tree for the function as follows: +// +// 1. It inserts a call to racefuncenter at the beginning of each function. +// 2. It inserts a call to racefuncexit at the end of each function. +// 3. It inserts a call to raceread before each memory read. +// 4. It inserts a call to racewrite before each memory write. +// +// The rewriting is not yet complete. Certain nodes are not rewritten +// but should be. + +#include <u.h> +#include <libc.h> +#include "go.h" + +// TODO(dvyukov): do not instrument initialization as writes: +// a := make([]int, 10) + +static void racewalklist(NodeList *l, NodeList **init); +static void racewalknode(Node **np, NodeList **init, int wr, int skip); +static int callinstr(Node **n, NodeList **init, int wr, int skip); +static Node* uintptraddr(Node *n); +static Node* basenod(Node *n); +static void foreach(Node *n, void(*f)(Node*, void*), void *c); +static void hascallspred(Node *n, void *c); +static Node* detachexpr(Node *n, NodeList **init); + +// Do not instrument the following packages at all, +// at best instrumentation would cause infinite recursion. +static const char *omit_pkgs[] = {"runtime", "runtime/race"}; +// Only insert racefuncenter/racefuncexit into the following packages. +// Memory accesses in the packages are either uninteresting or will cause false positives. +static const char *noinst_pkgs[] = {"sync", "sync/atomic"}; + +static int +ispkgin(const char **pkgs, int n) +{ + int i; + + if(myimportpath) { + for(i=0; i<n; i++) { + if(strcmp(myimportpath, pkgs[i]) == 0) + return 1; + } + } + return 0; +} + +void +racewalk(Node *fn) +{ + Node *nd; + Node *nodpc; + char s[1024]; + + if(ispkgin(omit_pkgs, nelem(omit_pkgs))) + return; + + if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) { + racewalklist(fn->nbody, nil); + // nothing interesting for race detector in fn->enter + racewalklist(fn->exit, nil); + } + + // nodpc is the PC of the caller as extracted by + // getcallerpc. We use -widthptr(FP) for x86. + // BUG: this will not work on arm. + nodpc = nod(OXXX, nil, nil); + *nodpc = *nodfp; + nodpc->type = types[TUINTPTR]; + nodpc->xoffset = -widthptr; + nd = mkcall("racefuncenter", T, nil, nodpc); + fn->enter = concat(list1(nd), fn->enter); + nd = mkcall("racefuncexit", T, nil); + fn->exit = list(fn->exit, nd); + + if(debug['W']) { + snprint(s, sizeof(s), "after racewalk %S", fn->nname->sym); + dumplist(s, fn->nbody); + snprint(s, sizeof(s), "enter %S", fn->nname->sym); + dumplist(s, fn->enter); + snprint(s, sizeof(s), "exit %S", fn->nname->sym); + dumplist(s, fn->exit); + } +} + +static void +racewalklist(NodeList *l, NodeList **init) +{ + NodeList *instr; + + for(; l; l = l->next) { + instr = nil; + racewalknode(&l->n, &instr, 0, 0); + if(init == nil) + l->n->ninit = concat(l->n->ninit, instr); + else + *init = concat(*init, instr); + } +} + +// walkexpr and walkstmt combined +// walks the tree and adds calls to the +// instrumentation code to top-level (statement) nodes' init +static void +racewalknode(Node **np, NodeList **init, int wr, int skip) +{ + Node *n, *n1; + NodeList *l; + NodeList *fini; + + n = *np; + + if(n == N) + return; + + if(debug['w'] > 1) + dump("racewalk-before", n); + setlineno(n); + if(init == nil || init == &n->ninit) + fatal("racewalk: bad init list"); + + racewalklist(n->ninit, nil); + + switch(n->op) { + default: + fatal("racewalk: unknown node type %O", n->op); + + case OASOP: + case OAS: + case OAS2: + case OAS2DOTTYPE: + case OAS2RECV: + case OAS2FUNC: + case OAS2MAPR: + racewalknode(&n->left, init, 1, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + + case OCFUNC: + // can't matter + goto ret; + + case OBLOCK: + if(n->list == nil) + goto ret; + + switch(n->list->n->op) { + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + // Blocks are used for multiple return function calls. + // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]} + // We don't want to instrument between the statements because it will + // smash the results. + racewalknode(&n->list->n, &n->ninit, 0, 0); + fini = nil; + racewalklist(n->list->next, &fini); + n->list = concat(n->list, fini); + break; + + default: + // Ordinary block, for loop initialization or inlined bodies. + racewalklist(n->list, nil); + break; + } + goto ret; + + case ODEFER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OPROC: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OCALLINTER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OCALLFUNC: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case OSWITCH: + if(n->ntest->op == OTYPESW) + // TODO(dvyukov): the expression can contain calls or reads. + return; + goto ret; + + case ONOT: + case OMINUS: + case OPLUS: + case OREAL: + case OIMAG: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case ODOTINTER: + racewalknode(&n->left, init, 0, 0); + goto ret; + + case ODOT: + racewalknode(&n->left, init, 0, 1); + callinstr(&n, init, wr, skip); + goto ret; + + case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND + racewalknode(&n->left, init, 0, 0); + callinstr(&n, init, wr, skip); + goto ret; + + case OIND: // *p + racewalknode(&n->left, init, 0, 0); + callinstr(&n, init, wr, skip); + goto ret; + + case OLEN: + case OCAP: + racewalknode(&n->left, init, 0, 0); + if(istype(n->left->type, TMAP)) { + // crashes on len(m[0]) or len(f()) + SET(n1); + USED(n1); + /* + n1 = nod(OADDR, n->left, N); + n1 = conv(n1, types[TUNSAFEPTR]); + n1 = conv(n1, ptrto(ptrto(types[TINT8]))); + n1 = nod(OIND, n1, N); + n1 = nod(OIND, n1, N); + typecheck(&n1, Erv); + callinstr(&n1, init, 0, skip); + */ + } + goto ret; + + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case OOR: + case OXOR: + case OSUB: + case OMUL: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + case OGT: + case OADD: + case OCOMPLEX: + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, wr, 0); + goto ret; + + case OANDAND: + case OOROR: + racewalknode(&n->left, init, wr, 0); + // It requires more complex tree transformation, + // because we don't know whether it will be executed or not. + //racewalknode(&n->right, init, wr, 0); + goto ret; + + case ONAME: + callinstr(&n, init, wr, skip); + goto ret; + + case OCONV: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case OCONVNOP: + racewalknode(&n->left, init, wr, 0); + goto ret; + + case ODIV: + case OMOD: + // TODO(dvyukov): add a test for this + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, wr, 0); + goto ret; + + case OINDEX: + if(!isfixedarray(n->left->type)) + racewalknode(&n->left, init, 0, 0); + else if(!islvalue(n->left)) { + // index of unaddressable array, like Map[k][i]. + racewalknode(&n->left, init, wr, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + } + racewalknode(&n->right, init, 0, 0); + if(n->left->type->etype != TSTRING) + callinstr(&n, init, wr, skip); + goto ret; + + case OSLICE: + case OSLICEARR: + // Seems to only lead to double instrumentation. + //racewalknode(&n->left, init, 0, 0); + goto ret; + + case OADDR: + racewalknode(&n->left, init, 0, 1); + goto ret; + + case OEFACE: + racewalknode(&n->left, init, 0, 0); + racewalknode(&n->right, init, 0, 0); + goto ret; + + // should not appear in AST by now + case OSEND: + case ORECV: + case OCLOSE: + case ONEW: + case OXCASE: + case OXFALL: + case OCASE: + case OPANIC: + case ORECOVER: + case OCONVIFACE: + yyerror("racewalk: %O must be lowered by now", n->op); + goto ret; + + // just do generic traversal + case OFOR: + case OIF: + case OCALLMETH: + case ORETURN: + case OSELECT: + case OEMPTY: + goto ret; + + // does not require instrumentation + case OINDEXMAP: // implemented in runtime + case OPRINT: // don't bother instrumenting it + case OPRINTN: // don't bother instrumenting it + case OPARAM: // it appears only in fn->exit to copy heap params back + goto ret; + + // unimplemented + case OCMPSTR: + case OADDSTR: + case OSLICESTR: + case OAPPEND: + case OCOPY: + case OMAKECHAN: + case OMAKEMAP: + case OMAKESLICE: + case ORUNESTR: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case OCMPIFACE: + case OARRAYLIT: + case OMAPLIT: + case OSTRUCTLIT: + case OCLOSURE: + case ODOTTYPE: + case ODOTTYPE2: + case OCALL: + case OBREAK: + case ODCL: + case OCONTINUE: + case OFALL: + case OGOTO: + case OLABEL: + case ODCLCONST: + case ODCLTYPE: + case OLITERAL: + case ORANGE: + case OTYPE: + case ONONAME: + case OINDREG: + case OCOM: + case ODOTMETH: + case OITAB: + case OEXTEND: + case OHMUL: + case OLROT: + case ORROTC: + goto ret; + } + +ret: + if(n->op != OBLOCK) // OBLOCK is handled above in a special way. + racewalklist(n->list, init); + l = nil; + racewalknode(&n->ntest, &l, 0, 0); + n->ninit = concat(n->ninit, l); + l = nil; + racewalknode(&n->nincr, &l, 0, 0); + n->ninit = concat(n->ninit, l); + racewalklist(n->nbody, nil); + racewalklist(n->nelse, nil); + racewalklist(n->rlist, nil); + + *np = n; +} + +static int +isartificial(Node *n) +{ + // compiler-emitted artificial things that we do not want to instrument, + // cant' possibly participate in a data race. + if(n->op == ONAME && n->sym != S && n->sym->name != nil) { + if(strcmp(n->sym->name, "_") == 0) + return 1; + // autotmp's are always local + if(strncmp(n->sym->name, "autotmp_", sizeof("autotmp_")-1) == 0) + return 1; + // statictmp's are read-only + if(strncmp(n->sym->name, "statictmp_", sizeof("statictmp_")-1) == 0) + return 1; + // go.itab is accessed only by the compiler and runtime (assume safe) + if(n->sym->pkg && n->sym->pkg->name && strcmp(n->sym->pkg->name, "go.itab") == 0) + return 1; + } + return 0; +} + +static int +callinstr(Node **np, NodeList **init, int wr, int skip) +{ + Node *f, *b, *n; + Type *t, *t1; + int class, res, hascalls; + + n = *np; + //print("callinstr for %+N [ %O ] etype=%E class=%d\n", + // n, n->op, n->type ? n->type->etype : -1, n->class); + + if(skip || n->type == T || n->type->etype >= TIDEAL) + return 0; + t = n->type; + if(isartificial(n)) + return 0; + if(t->etype == TSTRUCT) { + // PARAMs w/o PHEAP are not interesting. + if(n->class == PPARAM || n->class == PPARAMOUT) + return 0; + res = 0; + hascalls = 0; + foreach(n, hascallspred, &hascalls); + if(hascalls) { + n = detachexpr(n, init); + *np = n; + } + for(t1=t->type; t1; t1=t1->down) { + if(t1->sym && strcmp(t1->sym->name, "_")) { + n = treecopy(n); + f = nod(OXDOT, n, newname(t1->sym)); + f->type = t1; + if(f->type->etype == TFIELD) + f->type = f->type->type; + if(callinstr(&f, init, wr, 0)) { + typecheck(&f, Erv); + res = 1; + } + } + } + return res; + } + + b = basenod(n); + // it skips e.g. stores to ... parameter array + if(isartificial(b)) + return 0; + class = b->class; + // BUG: we _may_ want to instrument PAUTO sometimes + // e.g. if we've got a local variable/method receiver + // that has got a pointer inside. Whether it points to + // the heap or not is impossible to know at compile time + if((class&PHEAP) || class == PPARAMREF || class == PEXTERN + || b->type->etype == TARRAY || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) { + hascalls = 0; + foreach(n, hascallspred, &hascalls); + if(hascalls) { + n = detachexpr(n, init); + *np = n; + } + n = treecopy(n); + f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n)); + *init = list(*init, f); + return 1; + } + return 0; +} + +static Node* +uintptraddr(Node *n) +{ + Node *r; + + r = nod(OADDR, n, N); + r = conv(r, types[TUNSAFEPTR]); + r = conv(r, types[TUINTPTR]); + return r; +} + +static Node* +basenod(Node *n) +{ + for(;;) { + if(n->op == ODOT || n->op == OXDOT || n->op == OCONVNOP || n->op == OCONV || n->op == OPAREN) { + n = n->left; + continue; + } + if(n->op == OINDEX) { + n = n->left; + continue; + } + break; + } + return n; +} + +static Node* +detachexpr(Node *n, NodeList **init) +{ + Node *addr, *as, *ind, *l; + + addr = nod(OADDR, n, N); + l = temp(ptrto(n->type)); + as = nod(OAS, l, addr); + typecheck(&as, Etop); + walkexpr(&as, init); + *init = list(*init, as); + ind = nod(OIND, l, N); + typecheck(&ind, Erv); + walkexpr(&ind, init); + return ind; +} + +static void +foreachnode(Node *n, void(*f)(Node*, void*), void *c) +{ + if(n) + f(n, c); +} + +static void +foreachlist(NodeList *l, void(*f)(Node*, void*), void *c) +{ + for(; l; l = l->next) + foreachnode(l->n, f, c); +} + +static void +foreach(Node *n, void(*f)(Node*, void*), void *c) +{ + foreachlist(n->ninit, f, c); + foreachnode(n->left, f, c); + foreachnode(n->right, f, c); + foreachlist(n->list, f, c); + foreachnode(n->ntest, f, c); + foreachnode(n->nincr, f, c); + foreachlist(n->nbody, f, c); + foreachlist(n->nelse, f, c); + foreachlist(n->rlist, f, c); +} + +static void +hascallspred(Node *n, void *c) +{ + switch(n->op) { + case OCALL: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + (*(int*)c)++; + } +} diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index d301e4e4f..50c4617c0 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -72,6 +72,15 @@ typecheckrange(Node *n) if(n->list->next) v2 = n->list->next->n; + // this is not only a optimization but also a requirement in the spec. + // "if the second iteration variable is the blank identifier, the range + // clause is equivalent to the same clause with only the first variable + // present." + if(isblank(v2)) { + n->list = list1(v1); + v2 = N; + } + if(v1->defn == n) v1->type = t1; else if(v1->type != T && assignop(t1, v1->type, &why) == 0) @@ -145,7 +154,7 @@ walkrange(Node *n) if(v2) { hp = temp(ptrto(n->type->type)); tmp = nod(OINDEX, ha, nodintconst(0)); - tmp->etype = 1; // no bounds check + tmp->bounded = 1; init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); } diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 62759bcba..b8eb79938 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -5,6 +5,7 @@ #include <u.h> #include <libc.h> #include "go.h" +#include "../../pkg/runtime/mgc0.h" /* * runtime interface and reflection data structures @@ -14,12 +15,13 @@ static NodeList* signatlist; static Sym* dtypesym(Type*); static Sym* weaktypesym(Type*); static Sym* dalgsym(Type*); +static Sym* dgcsym(Type*); static int sigcmp(Sig *a, Sig *b) { int i; - + i = strcmp(a->name, b->name); if(i != 0) return i; @@ -130,7 +132,12 @@ methodfunc(Type *f, Type *receiver) out = list(out, d); } - return functype(N, in, out); + t = functype(N, in, out); + if(f->nname) { + // Link to name of original method function. + t->nname = f->nname; + } + return t; } /* @@ -165,6 +172,8 @@ methods(Type *t) fatal("non-method on %T method %S %T\n", mt, f->sym, f); if (!getthisx(f->type)->type) fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f); + if(f->nointerface) + continue; method = f->sym; if(method == nil) @@ -203,22 +212,26 @@ methods(Type *t) // but we can generate more efficient code // using genembedtramp if all that is necessary // is a pointer adjustment and a JMP. + compiling_wrappers = 1; if(isptr[it->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) genembedtramp(it, f, a->isym, 1); else genwrapper(it, f, a->isym, 1); + compiling_wrappers = 0; } } if(!(a->tsym->flags & SymSiggen)) { a->tsym->flags |= SymSiggen; if(!eqtype(this, t)) { + compiling_wrappers = 1; if(isptr[t->etype] && isptr[this->etype] && f->embedded && !isifacemethod(f->type)) genembedtramp(t, f, a->tsym, 0); else genwrapper(t, f, a->tsym, 0); + compiling_wrappers = 0; } } } @@ -262,12 +275,12 @@ imethods(Type *t) else last->link = a; last = a; - + // Compiler can only refer to wrappers for // named interface types. if(t->sym == S) continue; - + // NOTE(rsc): Perhaps an oversight that // IfaceType.Method is not in the reflect data. // Generate the method body, so that compiled @@ -287,7 +300,7 @@ dimportpath(Pkg *p) static Pkg *gopkg; char *nam; Node *n; - + if(p->pathsym != S) return; @@ -303,9 +316,9 @@ dimportpath(Pkg *p) n->class = PEXTERN; n->xoffset = 0; p->pathsym = n->sym; - + gdatastring(n, p->path); - ggloblsym(n->sym, types[TSTRING]->width, 1); + ggloblsym(n->sym, types[TSTRING]->width, 1, 1); } static int @@ -319,7 +332,7 @@ dgopkgpath(Sym *s, int ot, Pkg *pkg) // that imports this one directly defines the symbol. if(pkg == localpkg) { static Sym *ns; - + if(ns == nil) ns = pkglookup("importpath.\"\".", mkpkg(strlit("go"))); return dsymptr(s, ot, ns, 0); @@ -343,7 +356,7 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) m = methods(t); if(t->sym == nil && m == nil) return off; - + // fill in *extraType pointer in header dsymptr(sym, ptroff, sym, off); @@ -367,9 +380,9 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) } // slice header - ot = dsymptr(s, ot, s, ot + widthptr + 2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot + widthptr + 2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); // methods for(a=m; a; a=a->link) { @@ -419,7 +432,7 @@ enum { KindString, KindStruct, KindUnsafePointer, - + KindNoPointers = 1<<7, }; @@ -454,19 +467,6 @@ kinds[] = [TUNSAFEPTR] = KindUnsafePointer, }; -static Sym* -typestruct(Type *t) -{ - // We use a weak reference to the reflect type - // to avoid requiring package reflect in every binary. - // If package reflect is available, the interface{} holding - // a runtime type will contain a *reflect.commonType. - // Otherwise it will use a nil type word but still be usable - // by package runtime (because we always use the memory - // after the interface value, not the interface value itself). - return pkglookup("*reflect.commonType", weaktypepkg); -} - int haspointers(Type *t) { @@ -521,6 +521,9 @@ dcommontype(Sym *s, int ot, Type *t) Sym *sptr, *algsym; static Sym *algarray; char *p; + + if(ot != 0) + fatal("dcommontype %d", ot); sizeofAlg = 4*widthptr; if(algarray == nil) @@ -536,31 +539,34 @@ dcommontype(Sym *s, int ot, Type *t) else sptr = weaktypesym(ptrto(t)); - // empty interface pointing at this type. - // all the references that we emit are *interface{}; - // they point here. - ot = rnd(ot, widthptr); - ot = dsymptr(s, ot, typestruct(t), 0); - ot = dsymptr(s, ot, s, 2*widthptr); - // ../../pkg/reflect/type.go:/^type.commonType // actual type structure // type commonType struct { - // size uintptr; - // hash uint32; - // alg uint8; - // align uint8; - // fieldAlign uint8; - // kind uint8; - // string *string; - // *extraType; - // ptrToThis *Type + // size uintptr + // hash uint32 + // _ uint8 + // align uint8 + // fieldAlign uint8 + // kind uint8 + // alg unsafe.Pointer + // gc unsafe.Pointer + // string *string + // *extraType + // ptrToThis *Type // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); ot = duint8(s, ot, 0); // unused + + // runtime (and common sense) expects alignment to be a power of two. + i = t->align; + if(i == 0) + i = 1; + if((i&(i-1)) != 0) + fatal("invalid alignment %d for %T", t->align, t); ot = duint8(s, ot, t->align); // align ot = duint8(s, ot, t->align); // fieldAlign + i = kinds[t->etype]; if(t->etype == TARRAY && t->bound < 0) i = KindSlice; @@ -571,11 +577,12 @@ dcommontype(Sym *s, int ot, Type *t) ot = dsymptr(s, ot, algarray, alg*sizeofAlg); else ot = dsymptr(s, ot, algsym, 0); + ot = dsymptr(s, ot, dgcsym(t), 0); // gc p = smprint("%-uT", t); //print("dcommontype: %s\n", p); ot = dgostringptr(s, ot, p); // string free(p); - + // skip pointer to extraType, // which follows the rest of this type structure. // caller will fill in if needed. @@ -600,6 +607,39 @@ typesym(Type *t) } Sym* +tracksym(Type *t) +{ + char *p; + Sym *s; + + p = smprint("%-T.%s", t->outer, t->sym->name); + s = pkglookup(p, trackpkg); + free(p); + return s; +} + +Sym* +typelinksym(Type *t) +{ + char *p; + Sym *s; + + // %-uT is what the generated Type's string field says. + // It uses (ambiguous) package names instead of import paths. + // %-T is the complete, unambiguous type name. + // We want the types to end up sorted by string field, + // so use that first in the name, and then add :%-T to + // disambiguate. The names are a little long but they are + // discarded by the linker and do not end up in the symbol + // table of the final binary. + p = smprint("%-uT/%-T", t, t); + s = pkglookup(p, typelinkpkg); + //print("typelinksym: %s -> %+S\n", p, s); + free(p); + return s; +} + +Sym* typesymprefix(char *prefix, Type *t) { char *p; @@ -612,8 +652,8 @@ typesymprefix(char *prefix, Type *t) return s; } -Node* -typename(Type *t) +Sym* +typenamesym(Type *t) { Sym *s; Node *n; @@ -634,7 +674,16 @@ typename(Type *t) signatlist = list(signatlist, typenod(t)); } + return s->def->sym; +} + +Node* +typename(Type *t) +{ + Sym *s; + Node *n; + s = typenamesym(t); n = nod(OADDR, s->def, N); n->type = ptrto(s->def->type); n->addable = 1; @@ -660,10 +709,16 @@ static Sym* dtypesym(Type *t) { int ot, xt, n, isddd, dupok; - Sym *s, *s1, *s2; + Sym *s, *s1, *s2, *slink; Sig *a, *m; Type *t1, *tbase, *t2; + // Replace byte, rune aliases with real type. + // They've been separate internally to make error messages + // better, but we have to merge them in the reflect tables. + if(t == bytetype || t == runetype) + t = types[t->etype]; + if(isideal(t)) fatal("dtypesym %T", t); @@ -680,7 +735,7 @@ dtypesym(Type *t) tbase = t->type; dupok = tbase->sym == S; - if(compiling_runtime && + if(compiling_runtime && (tbase == types[tbase->etype] || tbase == bytetype || tbase == runetype || @@ -751,13 +806,13 @@ ok: // two slice headers: in and out. ot = rnd(ot, widthptr); - ot = dsymptr(s, ot, s, ot+2*(widthptr+2*4)); + ot = dsymptr(s, ot, s, ot+2*(widthptr+2*widthint)); n = t->thistuple + t->intuple; - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); - ot = dsymptr(s, ot, s, ot+1*(widthptr+2*4)+n*widthptr); - ot = duint32(s, ot, t->outtuple); - ot = duint32(s, ot, t->outtuple); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); + ot = dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr); + ot = duintxx(s, ot, t->outtuple, widthint); + ot = duintxx(s, ot, t->outtuple, widthint); // slice data for(t1=getthisx(t)->type; t1; t1=t1->down, n++) @@ -779,9 +834,9 @@ ok: // ../../pkg/runtime/type.go:/InterfaceType ot = dcommontype(s, ot, t); xt = ot - 2*widthptr; - ot = dsymptr(s, ot, s, ot+widthptr+2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); for(a=m; a; a=a->link) { // ../../pkg/runtime/type.go:/imethod ot = dgostringptr(s, ot, a->name); @@ -824,9 +879,9 @@ ok: } ot = dcommontype(s, ot, t); xt = ot - 2*widthptr; - ot = dsymptr(s, ot, s, ot+widthptr+2*4); - ot = duint32(s, ot, n); - ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+widthptr+2*widthint); + ot = duintxx(s, ot, n, widthint); + ot = duintxx(s, ot, n, widthint); for(t1=t->type; t1!=T; t1=t1->down) { // ../../pkg/runtime/type.go:/structField if(t1->sym && !t1->embedded) { @@ -837,7 +892,10 @@ ok: ot = dgopkgpath(s, ot, t1->sym->pkg); } else { ot = dgostringptr(s, ot, nil); - ot = dgostringptr(s, ot, nil); + if(t1->type->sym != S && t1->type->sym->pkg == builtinpkg) + ot = dgopkgpath(s, ot, localpkg); + else + ot = dgostringptr(s, ot, nil); } ot = dsymptr(s, ot, dtypesym(t1->type), 0); ot = dgostrlitptr(s, ot, t1->note); @@ -846,7 +904,24 @@ ok: break; } ot = dextratype(s, ot, t, xt); - ggloblsym(s, ot, dupok); + ggloblsym(s, ot, dupok, 1); + + // generate typelink.foo pointing at s = type.foo. + // The linker will leave a table of all the typelinks for + // types in the binary, so reflect can find them. + // We only need the link for unnamed composites that + // we want be able to find. + if(t->sym == S) { + switch(t->etype) { + case TARRAY: + case TCHAN: + case TMAP: + slink = typelinksym(t); + dsymptr(slink, 0, s, 0); + ggloblsym(slink, widthptr, dupok, 1); + } + } + return s; } @@ -899,12 +974,14 @@ dumptypestructs(void) // emit type structs for error and func(error) string. // The latter is the type of an auto-generated wrapper. dtypesym(ptrto(errortype)); - dtypesym(functype(nil, + dtypesym(functype(nil, list1(nod(ODCLFIELD, N, typenod(errortype))), list1(nod(ODCLFIELD, N, typenod(types[TSTRING]))))); - + // add paths for runtime and main, which 6l imports implicitly. dimportpath(runtimepkg); + if(flag_race) + dimportpath(racepkg); dimportpath(mkpkg(strlit("main"))); } } @@ -944,7 +1021,180 @@ dalgsym(Type *t) break; } - ggloblsym(s, ot, 1); + ggloblsym(s, ot, 1, 1); return s; } +static int +dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size) +{ + Type *t1; + vlong o, off2, fieldoffset; + + if(t->align > 0 && (*off % t->align) != 0) + fatal("dgcsym1: invalid initial alignment, %T", t); + + switch(t->etype) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TBOOL: + case TFLOAT32: + case TFLOAT64: + case TCOMPLEX64: + case TCOMPLEX128: + *off += t->width; + break; + + case TPTR32: + case TPTR64: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(!haspointers(t->type) || t->type->etype == TUINT8) { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } + *off += t->width; + break; + + case TCHAN: + case TUNSAFEPTR: + case TFUNC: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct Hmap* + case TMAP: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_MAP_PTR); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dtypesym(t), 0); + *off += t->width; + break; + + // struct { byte *str; int32 len; } + case TSTRING: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + ot = duintptr(s, ot, GC_STRING); + ot = duintptr(s, ot, *off); + *off += t->width; + break; + + // struct { Itab* tab; void* data; } + // struct { Type* type; void* data; } // When isnilinter(t)==true + case TINTER: + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(isnilinter(t)) { + ot = duintptr(s, ot, GC_EFACE); + ot = duintptr(s, ot, *off); + } else { + ot = duintptr(s, ot, GC_IFACE); + ot = duintptr(s, ot, *off); + } + *off += t->width; + break; + + case TARRAY: + if(t->bound < -1) + fatal("dgcsym1: invalid bound, %T", t); + if(isslice(t)) { + // struct { byte* array; uint32 len; uint32 cap; } + if(*off % widthptr != 0) + fatal("dgcsym1: invalid alignment, %T", t); + if(t->type->width != 0) { + ot = duintptr(s, ot, GC_SLICE); + ot = duintptr(s, ot, *off); + ot = dsymptr(s, ot, dgcsym(t->type), 0); + } else { + ot = duintptr(s, ot, GC_APTR); + ot = duintptr(s, ot, *off); + } + *off += t->width; + } else { + if(t->bound < 1 || !haspointers(t->type)) { + *off += t->width; + } else if(t->bound == 1) { + ot = dgcsym1(s, ot, t->type, off, stack_size); // recursive call of dgcsym1 + } else { + if(stack_size < GC_STACK_CAPACITY) { + ot = duintptr(s, ot, GC_ARRAY_START); // a stack push during GC + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->bound); + ot = duintptr(s, ot, t->type->width); + off2 = 0; + ot = dgcsym1(s, ot, t->type, &off2, stack_size+1); // recursive call of dgcsym1 + ot = duintptr(s, ot, GC_ARRAY_NEXT); // a stack pop during GC + } else { + ot = duintptr(s, ot, GC_REGION); + ot = duintptr(s, ot, *off); + ot = duintptr(s, ot, t->width); + ot = dsymptr(s, ot, dgcsym(t), 0); + } + *off += t->width; + } + } + break; + + case TSTRUCT: + o = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + fieldoffset = t1->width; + *off += fieldoffset - o; + ot = dgcsym1(s, ot, t1->type, off, stack_size); // recursive call of dgcsym1 + o = fieldoffset + t1->type->width; + } + *off += t->width - o; + break; + + default: + fatal("dgcsym1: unexpected type %T", t); + } + + return ot; +} + +static Sym* +dgcsym(Type *t) +{ + int ot; + vlong off; + Sym *s; + + s = typesymprefix(".gc", t); + if(s->flags & SymGcgen) + return s; + s->flags |= SymGcgen; + + ot = 0; + off = 0; + ot = duintptr(s, ot, t->width); + ot = dgcsym1(s, ot, t, &off, 0); + ot = duintptr(s, ot, GC_END); + ggloblsym(s, ot, 1, 1); + + if(t->align > 0) + off = rnd(off, t->align); + if(off != t->width) + fatal("dgcsym: off=%lld, size=%lld, type %T", off, t->width, t); + + return s; +} diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 15a61d9ef..c49d05c5c 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -45,8 +45,7 @@ func appendslice(typ *byte, x any, y []any) any func appendstr(typ *byte, x []byte, y string) []byte func cmpstring(string, string) int -func slicestring(string, int, int) string -func slicestring1(string, int) string +func eqstring(string, string) bool func intstring(int64) string func slicebytetostring([]byte) string func slicerunetostring([]rune) string @@ -54,14 +53,15 @@ func stringtoslicebyte(string) []byte func stringtoslicerune(string) []rune func stringiter(string, int) int func stringiter2(string, int) (retk int, retv rune) -func copy(to any, fr any, wid uint32) int +func copy(to any, fr any, wid uintptr) int func slicestringcopy(to any, fr any) int // interface conversions +func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte) func convI2E(elem any) (ret any) func convI2I(typ *byte, elem any) (ret any) func convT2E(typ *byte, elem any) (ret any) -func convT2I(typ *byte, typ2 *byte, elem any) (ret any) +func convT2I(typ *byte, typ2 *byte, cache **byte, elem any) (ret any) // interface type assertions x.(T) func assertE2E(typ *byte, iface any) (ret any) @@ -76,6 +76,8 @@ func assertI2I(typ *byte, iface any) (ret any) func assertI2I2(typ *byte, iface any) (ret any, ok bool) func assertI2T(typ *byte, iface any) (ret any) func assertI2T2(typ *byte, iface any) (ret any, ok bool) +func assertI2TOK(typ *byte, iface any) (ok bool) +func assertE2TOK(typ *byte, iface any) (ok bool) func ifaceeq(i1 any, i2 any) (ret bool) func efaceeq(i1 any, i2 any) (ret bool) @@ -89,7 +91,6 @@ func makemap(mapType *byte, hint int64) (hmap map[any]any) func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any) func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool) func mapassign1(mapType *byte, hmap map[any]any, key any, val any) -func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool) func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) func mapdelete(mapType *byte, hmap map[any]any, key any) func mapiternext(hiter *any) @@ -107,7 +108,7 @@ func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool -func newselect(size int) (sel *byte) +func newselect(size int32) (sel *byte) func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) @@ -117,11 +118,6 @@ func block() func makeslice(typ *byte, nel int64, cap int64) (ary []any) func growslice(typ *byte, old []any, n int64) (ary []any) -func sliceslice1(old []any, lb uint64, width uint64) (ary []any) -func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) -func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any) - -func closure() // has args, but compiler fills in func memequal(eq *bool, size uintptr, x, y *any) func memequal8(eq *bool, size uintptr, x, y *any) @@ -141,3 +137,9 @@ func int64tofloat64(int64) float64 func uint64tofloat64(uint64) float64 func complex128div(num complex128, den complex128) (quo complex128) + +// race detection +func racefuncenter(uintptr) +func racefuncexit() +func raceread(uintptr) +func racewrite(uintptr) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 8ace1d4ee..cd3de8c7b 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -62,7 +62,7 @@ typecheckselect(Node *sel) case OAS2RECV: // convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok - if(n->right->op != ORECV) { + if(n->rlist->n->op != ORECV) { yyerror("select assignment must have receive on right hand side"); break; } @@ -70,6 +70,7 @@ typecheckselect(Node *sel) n->left = n->list->n; n->ntest = n->list->next->n; n->right = n->rlist->n; + n->rlist = nil; break; case ORECV: @@ -146,7 +147,7 @@ walkselect(Node *sel) a = nod(OAS2, N, N); a->list = n->list; - a->rlist = n->rlist; + a->rlist = list1(n->right); n = a; typecheck(&n, Etop); break; @@ -296,15 +297,15 @@ walkselect(Node *sel) setlineno(cas); n = cas->left; r = nod(OIF, N, N); - r->nbody = cas->ninit; + r->ninit = cas->ninit; cas->ninit = nil; if(n != nil) { - r->nbody = concat(r->nbody, n->ninit); + r->ninit = concat(r->ninit, n->ninit); n->ninit = nil; } if(n == nil) { // selectdefault(sel *byte); - r->ntest = mkcall("selectdefault", types[TBOOL], &init, var); + r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var); } else { switch(n->op) { default: @@ -312,25 +313,25 @@ walkselect(Node *sel) case OSEND: // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); - n->left = safeexpr(n->left, &r->ninit); + n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit); n->right = localexpr(n->right, n->left->type->type, &r->ninit); n->right = nod(OADDR, n->right, N); n->right->etype = 1; // pointer does not escape typecheck(&n->right, Erv); r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], - &init, var, n->left, n->right); + &r->ninit, var, n->left, n->right); break; case OSELRECV: // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], - &init, var, n->right->left, n->left); + &r->ninit, var, n->right->left, n->left); break; case OSELRECV2: // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL], - &init, var, n->right->left, n->left, n->ntest); + &r->ninit, var, n->right->left, n->left, n->ntest); break; } } diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index c8796f8b7..353fc00ce 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -37,6 +37,12 @@ init1(Node *n, NodeList **out) for(l=n->list; l; l=l->next) init1(l->n, out); + if(n->left && n->type && n->left->op == OTYPE && n->class == PFUNC) { + // Methods called as Type.Method(receiver, ...). + // Definitions for method expressions are stored in type->nname. + init1(n->type->nname, out); + } + if(n->op != ONAME) return; switch(n->class) { @@ -78,6 +84,11 @@ init1(Node *n, NodeList **out) } n->initorder = InitPending; l = malloc(sizeof *l); + if(l == nil) { + flusherrors(); + yyerror("out of memory"); + errorexit(); + } l->next = initlist; l->n = n; l->end = nil; @@ -97,6 +108,13 @@ init1(Node *n, NodeList **out) case OAS: if(n->defn->left != n) goto bad; + if(isblank(n->defn->left) && candiscard(n->defn->right)) { + n->defn->op = OEMPTY; + n->defn->left = N; + n->defn->right = N; + break; + } + /* n->defn->dodata = 1; init1(n->defn->right, out); @@ -167,6 +185,11 @@ init2(Node *n, NodeList **out) init2list(n->rlist, out); init2list(n->nbody, out); init2list(n->nelse, out); + + if(n->op == OCLOSURE) + init2list(n->closure->nbody, out); + if(n->op == ODOTMETH) + init2(n->type->nname, out); } static void @@ -291,9 +314,9 @@ staticcopy(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + Array_array; gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); return 1; } // fall through @@ -394,9 +417,9 @@ staticassign(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + Array_array; gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - gdata(&n1, r->right, 4); + gdata(&n1, r->right, widthint); // Fall through to init underlying array. l = a; } @@ -747,7 +770,7 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) index = r->left; value = r->right; a = nod(OINDEX, var, index); - a->etype = 1; // no bounds checking + a->bounded = 1; // TODO need to check bounds? switch(value->op) { @@ -879,11 +902,11 @@ ctxt = 0; index = temp(types[TINT]); a = nod(OINDEX, vstat, index); - a->etype = 1; // no bounds checking + a->bounded = 1; a = nod(ODOT, a, newname(symb)); r = nod(OINDEX, vstat, index); - r->etype = 1; // no bounds checking + r->bounded = 1; r = nod(ODOT, r, newname(syma)); r = nod(OINDEX, var, r); @@ -930,7 +953,7 @@ void anylit(int ctxt, Node *n, Node *var, NodeList **init) { Type *t; - Node *a, *vstat; + Node *a, *vstat, *r; t = n->type; switch(n->op) { @@ -941,7 +964,14 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(!isptr[t->etype]) fatal("anylit: not ptr"); - a = nod(OAS, var, callnew(t->type)); + r = nod(ONEW, N, N); + r->typecheck = 1; + r->type = t; + r->esc = n->esc; + walkexpr(&r, init); + + a = nod(OAS, var, r); + typecheck(&a, Etop); *init = list(*init, a); @@ -1223,11 +1253,11 @@ slice: gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; - nodconst(&nod1, types[TINT32], nr->type->bound); - gdata(&nam, &nod1, types[TINT32]->width); + nodconst(&nod1, types[TINT], nr->type->bound); + gdata(&nam, &nod1, widthint); nam.xoffset += Array_cap-Array_nel; - gdata(&nam, &nod1, types[TINT32]->width); + gdata(&nam, &nod1, widthint); goto yes; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index bd53520df..c53eaf285 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -142,18 +142,32 @@ yyerror(char *fmt, ...) if(debug['x']) print("yyerror: yystate=%d yychar=%d\n", yystate, yychar); + // An unexpected EOF caused a syntax error. Use the previous + // line number since getc generated a fake newline character. + if(curio.eofnl) + lexlineno = prevlineno; + // only one syntax error per line if(lastsyntax == lexlineno) return; lastsyntax = lexlineno; - - if(strstr(fmt, "{ or {")) { + + if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || strstr(fmt, " or @")) { // The grammar has { and LBRACE but both show up as {. // Rewrite syntax error referring to "{ or {" to say just "{". strecpy(buf, buf+sizeof buf, fmt); p = strstr(buf, "{ or {"); if(p) memmove(p+1, p+6, strlen(p+6)+1); + + // The grammar has ? and @ but only for reading imports. + // Silence them in ordinary errors. + p = strstr(buf, " or ?"); + if(p) + memmove(p, p+5, strlen(p+5)+1); + p = strstr(buf, " or @"); + if(p) + memmove(p, p+5, strlen(p+5)+1); fmt = buf; } @@ -214,6 +228,8 @@ warnl(int line, char *fmt, ...) va_start(arg, fmt); adderr(line, fmt, arg); va_end(arg); + if(debug['m']) + flusherrors(); } void @@ -377,6 +393,7 @@ importdot(Pkg *opkg, Node *pack) Sym *s, *s1; uint32 h; int n; + char *pkgerror; n = 0; for(h=0; h<NHASH; h++) { @@ -389,12 +406,14 @@ importdot(Pkg *opkg, Node *pack) continue; s1 = lookup(s->name); if(s1->def != N) { - redeclare(s1, "during import"); + pkgerror = smprint("during import \"%Z\"", opkg->path); + redeclare(s1, pkgerror); continue; } s1->def = s->def; s1->block = s->block; s1->def->pack = pack; + s1->origpkg = opkg; n++; } } @@ -494,6 +513,31 @@ nod(int op, Node *nleft, Node *nright) return n; } +void +saveorignode(Node *n) +{ + Node *norig; + + if(n->orig != N) + return; + norig = nod(n->op, N, N); + *norig = *n; + n->orig = norig; +} + +// ispaddedfield returns whether the given field +// is followed by padding. For the case where t is +// the last field, total gives the size of the enclosing struct. +static int +ispaddedfield(Type *t, vlong total) +{ + if(t->etype != TFIELD) + fatal("ispaddedfield called non-field %T", t); + if(t->down == T) + return t->width + t->type->width != total; + return t->width + t->type->width != t->down->width; +} + int algtype1(Type *t, Type **bad) { @@ -571,8 +615,12 @@ algtype1(Type *t, Type **bad) } ret = AMEM; for(t1=t->type; t1!=T; t1=t1->down) { - if(isblanksym(t1->sym)) + // Blank fields and padding must be ignored, + // so need special compare. + if(isblanksym(t1->sym) || ispaddedfield(t1, t->width)) { + ret = -1; continue; + } a = algtype1(t1->type, bad); if(a == ANOEQ) return ANOEQ; // not comparable @@ -813,6 +861,7 @@ treecopy(Node *n) default: m = nod(OXXX, N, N); *m = *n; + m->orig = m; m->left = treecopy(n->left); m->right = treecopy(n->right); m->list = listtreecopy(n->list); @@ -1214,7 +1263,7 @@ assignop(Type *src, Type *dst, char **why) return 0; } if(src->etype == TINTER && dst->etype != TBLANK) { - if(why != nil) + if(why != nil && implements(dst, src, &missing, &have, &ptr)) *why = ": need type assertion"; return 0; } @@ -1379,6 +1428,7 @@ assignconv(Node *n, Type *t, char *context) r->type = t; r->typecheck = 1; r->implicit = 1; + r->orig = n->orig; return r; } @@ -2029,11 +2079,13 @@ cheapexpr(Node *n, NodeList **init) /* * return n in a local variable of type t if it is not already. + * the value is guaranteed not to change except by direct + * assignment to it. */ Node* localexpr(Node *n, Type *t, NodeList **init) { - if(n->op == ONAME && + if(n->op == ONAME && !n->addrtaken && (n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) && convertop(n->type, t, nil) == OCONVNOP) return n; @@ -2508,6 +2560,9 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) funcbody(fn); curfn = fn; + // wrappers where T is anonymous (struct{ NamedType }) can be duplicated. + if(rcvr->etype == TSTRUCT || isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT) + fn->dupok = 1; typecheck(&fn, Etop); typechecklist(fn->nbody, Etop); curfn = nil; @@ -2591,7 +2646,7 @@ genhash(Sym *sym, Type *t) Node *hashel; Type *first, *t1; int old_safemode; - int64 size, mul; + int64 size, mul, offend; if(debug['r']) print("genhash %S %T\n", sym, t); @@ -2664,7 +2719,7 @@ genhash(Sym *sym, Type *t) call->list = list(call->list, nh); call->list = list(call->list, nodintconst(t->type->width)); nx = nod(OINDEX, np, ni); - nx->etype = 1; // no bounds check + nx->bounded = 1; na = nod(OADDR, nx, N); na->etype = 1; // no escape to heap call->list = list(call->list, na); @@ -2677,22 +2732,21 @@ genhash(Sym *sym, Type *t) // Walk the struct using memhash for runs of AMEM // and calling specific hash functions for the others. first = T; + offend = 0; for(t1=t->type;; t1=t1->down) { - if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) { - if(first == T && !isblanksym(t1->sym)) + if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) { + offend = t1->width + t1->type->width; + if(first == T) first = t1; - continue; + // If it's a memory field but it's padded, stop here. + if(ispaddedfield(t1, t->width)) + t1 = t1->down; + else + continue; } // Run memhash for fields up to this one. - while(first != T && isblanksym(first->sym)) - first = first->down; if(first != T) { - if(first->down == t1) - size = first->type->width; - else if(t1 == T) - size = t->width - first->width; // first->width is offset - else - size = t1->width - first->width; // both are offsets + size = offend - first->width; // first->width is offset hashel = hashmem(first->type); // hashel(h, size, &p.first) call = nod(OCALL, hashel, N); @@ -2708,6 +2762,8 @@ genhash(Sym *sym, Type *t) } if(t1 == T) break; + if(isblanksym(t1->sym)) + continue; // Run hash for this field. hashel = hashfor(t1->type); @@ -2721,6 +2777,8 @@ genhash(Sym *sym, Type *t) call->list = list(call->list, na); fn->nbody = list(fn->nbody, call); } + // make sure body is not empty. + fn->nbody = list(fn->nbody, nod(ORETURN, N, N)); break; } @@ -2822,6 +2880,7 @@ geneq(Sym *sym, Type *t) Type *t1, *first; int old_safemode; int64 size; + int64 offend; if(debug['r']) print("geneq %S %T\n", sym, t); @@ -2875,9 +2934,9 @@ geneq(Sym *sym, Type *t) // if p[i] != q[i] { *eq = false; return } nx = nod(OINDEX, np, ni); - nx->etype = 1; // no bounds check + nx->bounded = 1; ny = nod(OINDEX, nq, ni); - ny->etype = 1; // no bounds check + ny->bounded = 1; nif = nod(OIF, N, N); nif->ntest = nod(ONE, nx, ny); @@ -2893,18 +2952,23 @@ geneq(Sym *sym, Type *t) case TSTRUCT: // Walk the struct using memequal for runs of AMEM // and calling specific equality tests for the others. + // Skip blank-named fields. first = T; + offend = 0; for(t1=t->type;; t1=t1->down) { - if(t1 != T && (isblanksym(t1->sym) || algtype1(t1->type, nil) == AMEM)) { - if(first == T && !isblanksym(t1->sym)) + if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) { + offend = t1->width + t1->type->width; + if(first == T) first = t1; - continue; + // If it's a memory field but it's padded, stop here. + if(ispaddedfield(t1, t->width)) + t1 = t1->down; + else + continue; } // Run memequal for fields up to this one. // TODO(rsc): All the calls to newname are wrong for // cross-package unexported fields. - while(first != T && isblanksym(first->sym)) - first = first->down; if(first != T) { if(first->down == t1) { fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); @@ -2915,16 +2979,15 @@ geneq(Sym *sym, Type *t) fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); } else { // More than two fields: use memequal. - if(t1 == T) - size = t->width - first->width; // first->width is offset - else - size = t1->width - first->width; // both are offsets + size = offend - first->width; // first->width is offset fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq)); } first = T; } if(t1 == T) break; + if(isblanksym(t1->sym)) + continue; // Check this field, which is not just memory. fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq)); @@ -3031,7 +3094,7 @@ implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr) for(im=iface->type; im; im=im->down) { imtype = methodfunc(im->type, 0); tm = ifacelookdot(im->sym, t, &followptr, 0); - if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) { + if(tm == T || tm->nointerface || !eqtype(methodfunc(tm->type, 0), imtype)) { if(tm == T) tm = ifacelookdot(im->sym, t, &followptr, 1); *m = im; @@ -3504,11 +3567,8 @@ umagic(Magic *m) Sym* ngotype(Node *n) { - if(n->sym != S && n->realtype != T) - if(strncmp(n->sym->name, "autotmp_", 8) != 0) - if(strncmp(n->sym->name, "statictmp_", 8) != 0) - return typename(n->realtype)->left->sym; - + if(n->type != T) + return typenamesym(n->type); return S; } @@ -3564,9 +3624,6 @@ mkpkg(Strlit *path) Pkg *p; int h; - if(isbadimport(path)) - errorexit(); - h = stringhash(path->s) & (nelem(phash)-1); for(p=phash[h]; p; p=p->link) if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0) @@ -3615,9 +3672,15 @@ addinit(Node **np, NodeList *init) n->ullman = UINF; } +static char* reservedimports[] = { + "go", + "type", +}; + int isbadimport(Strlit *path) { + int i; char *s; Rune r; @@ -3625,6 +3688,13 @@ isbadimport(Strlit *path) yyerror("import path contains NUL"); return 1; } + + for(i=0; i<nelem(reservedimports); i++) { + if(strcmp(path->s, reservedimports[i]) == 0) { + yyerror("import path \"%s\" is reserved and cannot be used", path->s); + return 1; + } + } s = path->s; while(*s) { diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index f1a95587f..a497b8622 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -117,12 +117,15 @@ exprcmp(Case *c1, Case *c2) n1 = c1->node->left; n2 = c2->node->left; + // sort by type (for switches on interface) ct = n1->val.ctype; - if(ct != n2->val.ctype) { - // invalid program, but return a sort - // order so that we can give a better - // error later. + if(ct != n2->val.ctype) return ct - n2->val.ctype; + if(!eqtype(n1->type, n2->type)) { + if(n1->type->vargen > n2->type->vargen) + return +1; + else + return -1; } // sort by constant value @@ -259,10 +262,11 @@ casebody(Node *sw, Node *typeswvar) Node *go, *br; int32 lno, needvar; - lno = setlineno(sw); if(sw->list == nil) return; + lno = setlineno(sw); + cas = nil; // cases stat = nil; // statements def = N; // defaults @@ -270,7 +274,7 @@ casebody(Node *sw, Node *typeswvar) for(l=sw->list; l; l=l->next) { n = l->n; - lno = setlineno(n); + setlineno(n); if(n->op != OXCASE) fatal("casebody %O", n->op); n->op = OCASE; @@ -378,6 +382,7 @@ mkcaselist(Node *sw, int arg) case Strue: case Sfalse: c->type = Texprvar; + c->hash = typehash(n->left->type); switch(consttype(n->left)) { case CTFLT: case CTINT: @@ -442,6 +447,10 @@ exprbsw(Case *c0, int ncase, int arg) n = c0->node; lno = setlineno(n); + if(assignop(n->left->type, exprname->type, nil) == OCONVIFACE || + assignop(exprname->type, n->left->type, nil) == OCONVIFACE) + goto snorm; + switch(arg) { case Strue: a = nod(OIF, N, N); @@ -457,6 +466,7 @@ exprbsw(Case *c0, int ncase, int arg) break; default: + snorm: a = nod(OIF, N, N); a->ntest = nod(OEQ, exprname, n->left); // if name == val typecheck(&a->ntest, Erv); @@ -520,6 +530,8 @@ exprswitch(Node *sw) exprname = temp(sw->ntest->type); cas = list1(nod(OAS, exprname, sw->ntest)); typechecklist(cas, Etop); + } else { + exprname = nodbool(arg == Strue); } c0 = mkcaselist(sw, arg); @@ -786,7 +798,6 @@ typeswitch(Node *sw) void walkswitch(Node *sw) { - /* * reorder the body into (OLIST, cases, statements) * cases have OGOTO into statements. @@ -813,7 +824,7 @@ typecheckswitch(Node *n) { int top, lno, ptr; char *nilonly; - Type *t, *missing, *have; + Type *t, *badtype, *missing, *have; NodeList *l, *ll; Node *ncase, *nvar; Node *def; @@ -839,10 +850,14 @@ typecheckswitch(Node *n) } else t = types[TBOOL]; if(t) { - if(!okforeq[t->etype] || isfixedarray(t)) + if(!okforeq[t->etype]) yyerror("cannot switch on %lN", n->ntest); - else if(t->etype == TARRAY) + else if(t->etype == TARRAY && !isfixedarray(t)) nilonly = "slice"; + else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ) + yyerror("cannot switch on %lN", n->ntest); + else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ) + yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype); else if(t->etype == TFUNC) nilonly = "func"; else if(t->etype == TMAP) @@ -889,7 +904,7 @@ typecheckswitch(Node *n) yyerror("%lN is not a type", ll->n); // reset to original type ll->n = n->ntest->right; - } else if(ll->n->type->etype != TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { + } else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) { if(have && !missing->broke && !have->broke) yyerror("impossible type switch case: %lN cannot have dynamic type %T" " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index cc4faf5a7..fbab85d03 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -31,6 +31,8 @@ static void checkassign(Node*); static void checkassignlist(NodeList*); static void stringtoarraylit(Node**); static Node* resolve(Node*); +static void checkdefergo(Node*); +static int checkmake(Type*, char*, Node*); static NodeList* typecheckdefstack; @@ -106,6 +108,27 @@ typekind(Type *t) } /* + * sprint_depchain prints a dependency chain + * of nodes into fmt. + * It is used by typecheck in the case of OLITERAL nodes + * to print constant definition loops. + */ +static void +sprint_depchain(Fmt *fmt, NodeList *stack, Node *cur, Node *first) +{ + NodeList *l; + + for(l = stack; l; l=l->next) { + if(l->n->op == cur->op) { + if(l->n != first) + sprint_depchain(fmt, l->next, l->n, first); + fmtprint(fmt, "\n\t%L: %N uses %N", l->n->lineno, l->n, cur); + return; + } + } +} + +/* * type check node *np. * replaces *np with a new pointer in some cases. * returns the final value of *np as a convenience. @@ -155,6 +178,24 @@ typecheck(Node **np, int top) } if(n->typecheck == 2) { + // Typechecking loop. Trying printing a meaningful message, + // otherwise a stack trace of typechecking. + switch(n->op) { + case ONAME: + // We can already diagnose variables used as types. + if((top & (Erv|Etype)) == Etype) + yyerror("%N is not a type", n); + break; + case OLITERAL: + if((top & (Erv|Etype)) == Etype) { + yyerror("%N is not a type", n); + break; + } + fmtstrinit(&fmt); + sprint_depchain(&fmt, tcstack, n, n); + yyerrorl(n->lineno, "constant definition loop%s", fmtstrflush(&fmt)); + break; + } if(nsavederrors+nerrors == 0) { fmtstrinit(&fmt); for(l=tcstack; l; l=l->next) @@ -165,7 +206,7 @@ typecheck(Node **np, int top) return n; } n->typecheck = 2; - + if(tcfree != nil) { l = tcfree; tcfree = l->next; @@ -206,6 +247,12 @@ callrecv(Node *n) case OCALLINTER: case OCALLFUNC: case ORECV: + case OCAP: + case OLEN: + case OCOPY: + case ONEW: + case OAPPEND: + case ODELETE: return 1; } @@ -249,7 +296,6 @@ typecheck1(Node **np, int top) } typecheckdef(n); - n->realtype = n->type; if(n->op == ONONAME) goto error; } @@ -331,8 +377,11 @@ reswitch: t->bound = -1; // slice } else if(l->op == ODDD) { t->bound = -100; // to be filled in - if(!(top&Ecomplit)) + if(!(top&Ecomplit) && !n->diag) { + t->broke = 1; + n->diag = 1; yyerror("use of [...] array outside of array literal"); + } } else { l = typecheck(&n->left, Erv); switch(consttype(l)) { @@ -351,8 +400,10 @@ reswitch: if(t->bound < 0) { yyerror("array bound must be non-negative"); goto error; - } else - overflow(v, types[TINT]); + } else if(doesoverflow(v, types[TINT])) { + yyerror("array bound is too large"); + goto error; + } } typecheck(&r, Etype); if(r->type == T) @@ -396,7 +447,7 @@ reswitch: ok |= Etype; n->op = OTYPE; n->type = tostruct(n->list); - if(n->type == T) + if(n->type == T || n->type->broke) goto error; n->list = nil; break; @@ -436,8 +487,11 @@ reswitch: goto ret; } if(!isptr[t->etype]) { - yyerror("invalid indirect of %lN", n->left); - goto error; + if(top & (Erv | Etop)) { + yyerror("invalid indirect of %lN", n->left); + goto error; + } + goto ret; } ok |= Erv; n->type = t->type; @@ -567,7 +621,10 @@ reswitch: n->left = l; n->right = r; } - } + // non-comparison operators on ideal bools should make them lose their ideal-ness + } else if(t == idealbool) + t = types[TBOOL]; + if(et == TSTRING) { if(iscmp[n->op]) { n->etype = n->op; @@ -587,6 +644,13 @@ reswitch: n->op = OCMPIFACE; } } + + if((op == ODIV || op == OMOD) && isconst(r, CTINT)) + if(mpcmpfixc(r->val.u.xval, 0) == 0) { + yyerror("division by zero"); + goto error; + } + n->type = t; goto ret; @@ -697,6 +761,10 @@ reswitch: n->op = ODOTPTR; checkwidth(t); } + if(isblank(n->right)) { + yyerror("cannot refer to blank field or method"); + goto error; + } if(!lookdot(n, t, 0)) { if(lookdot(n, t, 1)) yyerror("%N undefined (cannot refer to unexported field or method %S)", n, n->right->sym); @@ -735,14 +803,20 @@ reswitch: } if(n->type != T && n->type->etype != TINTER) if(!implements(n->type, t, &missing, &have, &ptr)) { - if(have) - yyerror("impossible type assertion: %lN cannot have dynamic type %T" - " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT", - l, n->type, missing->sym, have->sym, have->type, - missing->sym, missing->type); + if(have && have->sym == missing->sym) + yyerror("impossible type assertion:\n\t%T does not implement %T (wrong type for %S method)\n" + "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym, + have->sym, have->type, missing->sym, missing->type); + else if(ptr) + yyerror("impossible type assertion:\n\t%T does not implement %T (%S method requires pointer receiver)", + n->type, t, missing->sym); + else if(have) + yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n" + "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym, + have->sym, have->type, missing->sym, missing->type); else - yyerror("impossible type assertion: %lN cannot have dynamic type %T" - " (missing %S method)", l, n->type, missing->sym); + yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)", + n->type, t, missing->sym); goto error; } goto ret; @@ -762,11 +836,35 @@ reswitch: yyerror("invalid operation: %N (index of type %T)", n, t); goto error; + + case TSTRING: case TARRAY: defaultlit(&n->right, T); - if(n->right->type != T && !isint[n->right->type->etype]) - yyerror("non-integer array index %N", n->right); - n->type = t->type; + if(t->etype == TSTRING) + n->type = types[TUINT8]; + else + n->type = t->type; + why = "string"; + if(t->etype == TARRAY) { + if(isfixedarray(t)) + why = "array"; + else + why = "slice"; + } + if(n->right->type != T && !isint[n->right->type->etype]) { + yyerror("non-integer %s index %N", why, n->right); + break; + } + if(n->right->op == OLITERAL) { + if(mpgetfix(n->right->val.u.xval) < 0) + yyerror("invalid %s index %N (index must be non-negative)", why, n->right); + else if(isfixedarray(t) && t->bound > 0 && mpgetfix(n->right->val.u.xval) >= t->bound) + yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound); + else if(isconst(n->left, CTSTR) && mpgetfix(n->right->val.u.xval) >= n->left->val.u.sval->len) + yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len); + else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) + yyerror("invalid %s index %N (index too large)", why, n->right); + } break; case TMAP: @@ -777,13 +875,6 @@ reswitch: n->type = t->type; n->op = OINDEXMAP; break; - - case TSTRING: - defaultlit(&n->right, types[TUINT]); - if(n->right->type != T && !isint[n->right->type->etype]) - yyerror("non-integer string index %N", n->right); - n->type = types[TUINT8]; - break; } goto ret; @@ -843,7 +934,8 @@ reswitch: defaultlit(&n->left, T); defaultlit(&n->right->left, T); defaultlit(&n->right->right, T); - if(isfixedarray(n->left->type)) { + l = n->left; + if(isfixedarray(l->type)) { if(!islvalue(n->left)) { yyerror("invalid operation %N (slice of unaddressable value)", n); goto error; @@ -851,6 +943,26 @@ reswitch: n->left = nod(OADDR, n->left, N); n->left->implicit = 1; typecheck(&n->left, Erv); + l = n->left; + } + if((t = l->type) == T) + goto error; + tp = nil; + if(istype(t, TSTRING)) { + n->type = t; + n->op = OSLICESTR; + } else if(isptr[t->etype] && isfixedarray(t->type)) { + tp = t->type; + n->type = typ(TARRAY); + n->type->type = tp->type; + n->type->bound = -1; + dowidth(n->type); + n->op = OSLICEARR; + } else if(isslice(t)) { + n->type = t; + } else { + yyerror("cannot slice %N (type %T)", l, t); + goto error; } if(n->right->left != N) { if((t = n->right->left->type) == T) @@ -859,6 +971,18 @@ reswitch: yyerror("invalid slice index %N (type %T)", n->right->left, t); goto error; } + if(n->right->left->op == OLITERAL) { + if(mpgetfix(n->right->left->val.u.xval) < 0) { + yyerror("invalid slice index %N (index must be non-negative)", n->right->left); + goto error; + } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->left->val.u.xval) > tp->bound) { + yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->left, tp->bound); + goto error; + } else if(mpcmpfixfix(n->right->left->val.u.xval, maxintval[TINT]) > 0) { + yyerror("invalid slice index %N (index too large)", n->right->left); + goto error; + } + } } if(n->right->right != N) { if((t = n->right->right->type) == T) @@ -867,29 +991,28 @@ reswitch: yyerror("invalid slice index %N (type %T)", n->right->right, t); goto error; } + if(n->right->right->op == OLITERAL) { + if(mpgetfix(n->right->right->val.u.xval) < 0) { + yyerror("invalid slice index %N (index must be non-negative)", n->right->right); + goto error; + } else if(tp != nil && tp->bound > 0 && mpgetfix(n->right->right->val.u.xval) > tp->bound) { + yyerror("invalid slice index %N (out of bounds for %d-element array)", n->right->right, tp->bound); + goto error; + } else if(mpcmpfixfix(n->right->right->val.u.xval, maxintval[TINT]) > 0) { + yyerror("invalid slice index %N (index too large)", n->right->right); + goto error; + } + } } - l = n->left; - if((t = l->type) == T) + if(n->right->left != N + && n->right->right != N + && n->right->left->op == OLITERAL + && n->right->right->op == OLITERAL + && mpcmpfixfix(n->right->left->val.u.xval, n->right->right->val.u.xval) > 0) { + yyerror("inverted slice index %N > %N", n->right->left, n->right->right); goto error; - if(istype(t, TSTRING)) { - n->type = t; - n->op = OSLICESTR; - goto ret; - } - if(isptr[t->etype] && isfixedarray(t->type)) { - n->type = typ(TARRAY); - n->type->type = t->type->type; - n->type->bound = -1; - dowidth(n->type); - n->op = OSLICEARR; - goto ret; - } - if(isslice(t)) { - n->type = t; - goto ret; } - yyerror("cannot slice %N (type %T)", l, t); - goto error; + goto ret; /* * call and call like @@ -916,8 +1039,11 @@ reswitch: defaultlit(&n->left, T); l = n->left; if(l->op == OTYPE) { - if(n->isddd || l->type->bound == -100) - yyerror("invalid use of ... in type conversion", l); + if(n->isddd || l->type->bound == -100) { + if(!l->type->broke) + yyerror("invalid use of ... in type conversion", l); + n->diag = 1; + } // pick off before type-checking arguments ok |= Erv; // turn CALL(type, arg) into CONV(arg) w/ type @@ -929,7 +1055,7 @@ reswitch: goto doconv; } - if(count(n->list) == 1) + if(count(n->list) == 1 && !n->isddd) typecheck(&n->list->n, Erv | Efnstruct); else typechecklist(n->list, Erv); @@ -1011,10 +1137,12 @@ reswitch: if(!iscomplex[t->etype]) goto badcall1; if(isconst(l, CTCPLX)){ + r = n; if(n->op == OREAL) n = nodfltconst(&l->val.u.cval->real); else n = nodfltconst(&l->val.u.cval->imag); + n->orig = r; } n->type = types[cplxsubtype(t->etype)]; goto ret; @@ -1074,7 +1202,9 @@ reswitch: } if(l->op == OLITERAL && r->op == OLITERAL) { // make it a complex literal - n = nodcplxlit(l->val, r->val); + r = nodcplxlit(l->val, r->val); + r->orig = n; + n = r; } n->type = t; goto ret; @@ -1214,17 +1344,25 @@ reswitch: case OCONV: doconv: ok |= Erv; + saveorignode(n); typecheck(&n->left, Erv | (top & (Eindir | Eiota))); convlit1(&n->left, n->type, 1); if((t = n->left->type) == T || n->type == T) goto error; if((n->op = convertop(t, n->type, &why)) == 0) { - yyerror("cannot convert %lN to type %T%s", n->left, n->type, why); + if(!n->diag && !n->type->broke) { + yyerror("cannot convert %lN to type %T%s", n->left, n->type, why); + n->diag = 1; + } n->op = OCONV; } switch(n->op) { case OCONVNOP: if(n->left->op == OLITERAL) { + r = nod(OXXX, N, N); + n->op = OCONV; + n->orig = r; + *r = *n; n->op = OLITERAL; n->val = n->left->val; } @@ -1267,22 +1405,20 @@ reswitch: l = args->n; args = args->next; typecheck(&l, Erv); - defaultlit(&l, types[TINT]); r = N; if(args != nil) { r = args->n; args = args->next; typecheck(&r, Erv); - defaultlit(&r, types[TINT]); } if(l->type == T || (r && r->type == T)) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer len argument to make(%T)", t); + et = checkmake(t, "len", l) < 0; + et |= r && checkmake(t, "cap", r) < 0; + if(et) goto error; - } - if(r && !isint[r->type->etype]) { - yyerror("non-integer cap argument to make(%T)", t); + if(isconst(l, CTINT) && r && isconst(r, CTINT) && mpcmpfixfix(l->val.u.xval, r->val.u.xval) > 0) { + yyerror("len larger than cap in make(%T)", t); goto error; } n->left = l; @@ -1298,10 +1434,8 @@ reswitch: defaultlit(&l, types[TINT]); if(l->type == T) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer size argument to make(%T)", t); + if(checkmake(t, "size", l) < 0) goto error; - } n->left = l; } else n->left = nodintconst(0); @@ -1317,10 +1451,8 @@ reswitch: defaultlit(&l, types[TINT]); if(l->type == T) goto error; - if(!isint[l->type->etype]) { - yyerror("non-integer buffer argument to make(%T)", t); + if(checkmake(t, "buffer", l) < 0) goto error; - } n->left = l; } else n->left = nodintconst(0); @@ -1402,6 +1534,21 @@ reswitch: fatal("OITAB of %T", t); n->type = ptrto(types[TUINTPTR]); goto ret; + + case OCLOSUREVAR: + ok |= Erv; + goto ret; + + case OCFUNC: + ok |= Erv; + typecheck(&n->left, Erv); + n->type = types[TUINTPTR]; + goto ret; + + case OCONVNOP: + ok |= Erv; + typecheck(&n->left, Erv); + goto ret; /* * statements @@ -1428,12 +1575,15 @@ reswitch: case ODEFER: ok |= Etop; - typecheck(&n->left, Etop); + typecheck(&n->left, Etop|Erv); + if(!n->left->diag) + checkdefergo(n); goto ret; case OPROC: ok |= Etop; - typecheck(&n->left, Etop|Eproc); + typecheck(&n->left, Etop|Eproc|Erv); + checkdefergo(n); goto ret; case OFOR: @@ -1530,9 +1680,7 @@ ret: } } - // TODO(rsc): should not need to check importpkg, - // but reflect mentions unsafe.Pointer. - if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR) + if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR) yyerror("cannot use unsafe.Pointer"); evconst(n); @@ -1555,7 +1703,7 @@ ret: } if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) { if(n->diag == 0) { - yyerror("%N not used", n); + yyerror("%N evaluated but not used", n); n->diag = 1; } goto error; @@ -1579,6 +1727,56 @@ out: } static void +checkdefergo(Node *n) +{ + char *what; + + what = "defer"; + if(n->op == OPROC) + what = "go"; + + switch(n->left->op) { + case OCALLINTER: + case OCALLMETH: + case OCALLFUNC: + case OCLOSE: + case OCOPY: + case ODELETE: + case OPANIC: + case OPRINT: + case OPRINTN: + case ORECOVER: + // ok + break; + case OAPPEND: + case OCAP: + case OCOMPLEX: + case OIMAG: + case OLEN: + case OMAKE: + case OMAKESLICE: + case OMAKECHAN: + case OMAKEMAP: + case ONEW: + case OREAL: + case OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof + if(n->left->orig != N && n->left->orig->op == OCONV) + goto conv; + yyerror("%s discards result of %N", what, n->left); + break; + default: + conv: + if(!n->diag) { + // The syntax made sure it was a call, so this must be + // a conversion. + n->diag = 1; + yyerror("%s requires function call, not conversion", what); + } + break; + } +} + +static void implicitstar(Node **nn) { Type *t; @@ -1682,7 +1880,7 @@ lookdot1(Node *errnode, Sym *s, Type *t, Type *f, int dostrcmp) static int looktypedot(Node *n, Type *t, int dostrcmp) { - Type *f1, *f2, *tt; + Type *f1, *f2; Sym *s; s = n->right->sym; @@ -1692,8 +1890,6 @@ looktypedot(Node *n, Type *t, int dostrcmp) if(f1 == T) return 0; - if(f1->width == BADWIDTH) - fatal("lookdot badwidth %T %p", f1, f1); n->right = methodname(n->right, t); n->xoffset = f1->width; n->type = f1->type; @@ -1701,11 +1897,9 @@ looktypedot(Node *n, Type *t, int dostrcmp) return 1; } - tt = t; - if(t->sym == S && isptr[t->etype]) - tt = t->type; - - f2 = methtype(tt, 0); + // Find the base type: methtype will fail if t + // is not of the form T or *T. + f2 = methtype(t, 0); if(f2 == T) return 0; @@ -1769,6 +1963,7 @@ lookdot(Node *n, Type *t, int dostrcmp) fatal("lookdot badwidth %T %p", f1, f1); n->xoffset = f1->width; n->type = f1->type; + n->paramfld = f1; if(t->etype == TINTER) { if(isptr[n->left->type->etype]) { n->left = nod(OIND, n->left, N); // implicitstar @@ -2129,10 +2324,10 @@ static void typecheckcomplit(Node **np) { int bad, i, len, nerr; - Node *l, *n, *r, **hash; + Node *l, *n, *norig, *r, **hash; NodeList *ll; Type *t, *f; - Sym *s; + Sym *s, *s1; int32 lno; ulong nhash; Node *autohash[101]; @@ -2146,14 +2341,18 @@ typecheckcomplit(Node **np) yyerror("missing type in composite literal"); goto error; } - + + // Save original node (including n->right) + norig = nod(n->op, N, N); + *norig = *n; + setlineno(n->right); l = typecheck(&n->right /* sic */, Etype|Ecomplit); if((t = l->type) == T) goto error; nerr = nerrors; n->type = t; - + if(isptr[t->etype]) { // For better or worse, we don't allow pointers as the composite literal type, // except when using the &T syntax, which sets implicit on the OIND. @@ -2161,13 +2360,12 @@ typecheckcomplit(Node **np) yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type); goto error; } - // Also, the underlying type must be a struct, map, slice, or array. if(!iscomptype(t)) { yyerror("invalid pointer type %T for composite literal", t); goto error; } - t = t->type; + t = t->type; } switch(t->etype) { @@ -2298,9 +2496,11 @@ typecheckcomplit(Node **np) // Sym might have resolved to name in other top-level // package, because of import dot. Redirect to correct sym // before we do the lookup. - if(s->pkg != localpkg && exportname(s->name)) - s = lookup(s->name); - + if(s->pkg != localpkg && exportname(s->name)) { + s1 = lookup(s->name); + if(s1->origpkg == s->pkg) + s = s1; + } f = lookdot1(nil, s, t, t->type, 0); if(f == nil) { yyerror("unknown %T field '%S' in struct literal", t, s); @@ -2323,6 +2523,7 @@ typecheckcomplit(Node **np) if(nerr != nerrors) goto error; + n->orig = norig; if(isptr[n->type->etype]) { n = nod(OPTRLIT, n, N); n->typecheck = 1; @@ -2331,6 +2532,7 @@ typecheckcomplit(Node **np) n->left->typecheck = 1; } + n->orig = norig; *np = n; lineno = lno; return; @@ -2527,7 +2729,6 @@ typecheckas2(Node *n) goto common; case ORECV: n->op = OAS2RECV; - n->right = n->rlist->n; goto common; case ODOTTYPE: n->op = OAS2DOTTYPE; @@ -2572,7 +2773,7 @@ typecheckfunc(Node *n) t->nname = n->nname; rcvr = getthisx(t)->type; if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) - addmethod(n->shortname->sym, t, 1); + addmethod(n->shortname->sym, t, 1, n->nname->nointerface); } static void @@ -2715,6 +2916,7 @@ typecheckdeftype(Node *n) typecheck(&n->ntype, Etype); if((t = n->ntype->type) == T) { n->diag = 1; + n->type = T; goto ret; } if(n->type == T) { @@ -2828,14 +3030,14 @@ typecheckdef(Node *n) yyerror("xxx"); } typecheck(&e, Erv | Eiota); - if(e->type != T && e->op != OLITERAL) { - yyerror("const initializer must be constant"); - goto ret; - } if(isconst(e, CTNIL)) { yyerror("const initializer cannot be nil"); goto ret; } + if(e->type != T && e->op != OLITERAL || !isgoconst(e)) { + yyerror("const initializer %N is not a constant", e); + goto ret; + } t = n->type; if(t != T) { if(!okforconst[t->etype]) { @@ -2912,3 +3114,33 @@ ret: n->walkdef = 1; return n; } + +static int +checkmake(Type *t, char *arg, Node *n) +{ + if(n->op == OLITERAL) { + n->val = toint(n->val); + if(mpcmpfixc(n->val.u.xval, 0) < 0) { + yyerror("negative %s argument in make(%T)", arg, t); + return -1; + } + if(mpcmpfixfix(n->val.u.xval, maxintval[TINT]) > 0) { + yyerror("%s argument too large in make(%T)", arg, t); + return -1; + } + + // Delay defaultlit until after we've checked range, to avoid + // a redundant "constant NNN overflows int" error. + defaultlit(&n, types[TINT]); + return 0; + } + + // Defaultlit still necessary for non-constant: n might be 1<<k. + defaultlit(&n, types[TINT]); + + if(!isint[n->type->etype]) { + yyerror("non-integer %s argument in make(%T) - %T", arg, t, n->type); + return -1; + } + return 0; +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index a4edc9062..de2105ed3 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,7 +21,13 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); +static Node* sliceany(Node*, NodeList**); static void walkcompare(Node**, NodeList**); +static void walkrotate(Node**); +static void walkmul(Node**, NodeList**); +static void walkdiv(Node**, NodeList**); +static int bounded(Node*, int64); +static Mpint mpzero; // can this code branch reach the end // without an unconditional RETURN @@ -178,8 +184,8 @@ walkstmt(Node **np) dump("nottop", n); break; - case OASOP: case OAS: + case OASOP: case OAS2: case OAS2DOTTYPE: case OAS2RECV: @@ -368,9 +374,10 @@ walkexpr(Node **np, NodeList **init) NodeList *ll, *lr, *lpost; Type *t; int et; - int64 v, v1, v2, len; + int64 v; int32 lno; - Node *n, *fn; + Node *n, *fn, *n1, *n2; + Sym *sym; char buf[100], *p; n = *np; @@ -409,7 +416,7 @@ walkexpr(Node **np, NodeList **init) switch(n->op) { default: dump("walk", n); - fatal("walkexpr: switch 1 unknown op %N", n); + fatal("walkexpr: switch 1 unknown op %+hN", n); break; case OTYPE: @@ -424,14 +431,23 @@ walkexpr(Node **np, NodeList **init) case OCOM: case OREAL: case OIMAG: - case ODOT: - case ODOTPTR: case ODOTMETH: case ODOTINTER: case OIND: walkexpr(&n->left, init); goto ret; + case ODOT: + case ODOTPTR: + usefield(n); + walkexpr(&n->left, init); + goto ret; + + case OEFACE: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + case OITAB: walkexpr(&n->left, init); goto ret; @@ -454,19 +470,34 @@ walkexpr(Node **np, NodeList **init) case OLSH: case ORSH: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + shiftwalked: + t = n->left->type; + n->bounded = bounded(n->right, 8*t->width); + if(debug['m'] && n->etype && !isconst(n->right, CTINT)) + warn("shift bounds check elided"); + goto ret; + case OAND: - case OOR: - case OXOR: case OSUB: - case OMUL: + case OHMUL: case OLT: case OLE: case OGE: case OGT: case OADD: case OCOMPLEX: + case OLROT: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + + case OOR: + case OXOR: walkexpr(&n->left, init); walkexpr(&n->right, init); + walkrotate(&n); goto ret; case OEQ: @@ -505,6 +536,11 @@ walkexpr(Node **np, NodeList **init) n->addable = 1; goto ret; + case OCLOSUREVAR: + case OCFUNC: + n->addable = 1; + goto ret; + case ONAME: if(!(n->class & PHEAP) && n->class != PPARAMREF) n->addable = 1; @@ -525,10 +561,12 @@ walkexpr(Node **np, NodeList **init) if(n->list && n->list->n->op == OAS) goto ret; + /* if(n->left->op == OCLOSURE) { walkcallclosure(n, init); t = n->left->type; } + */ walkexpr(&n->left, init); walkexprlist(n->list, init); @@ -656,6 +694,31 @@ walkexpr(Node **np, NodeList **init) n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); + if(isblank(n->list->n) && !isinter(r->type)) { + strcpy(buf, "assert"); + p = buf+strlen(buf); + if(isnilinter(r->left->type)) + *p++ = 'E'; + else + *p++ = 'I'; + *p++ = '2'; + *p++ = 'T'; + *p++ = 'O'; + *p++ = 'K'; + *p = '\0'; + + fn = syslook(buf, 1); + ll = list1(typename(r->type)); + ll = list(ll, r->left); + argtype(fn, r->left->type); + n1 = nod(OCALL, fn, N); + n1->list = ll; + n = nod(OAS, n->list->next->n, n1); + typecheck(&n, Etop); + walkexpr(&n, init); + goto ret; + } + r->op = ODOTTYPE2; walkexpr(&r, init); ll = ascompatet(n->op, n->list, &r->type, 0, init); @@ -694,10 +757,22 @@ walkexpr(Node **np, NodeList **init) goto ret; case OCONVIFACE: + walkexpr(&n->left, init); + + // Optimize convT2E as a two-word copy when T is uintptr-shaped. + if(!isinter(n->left->type) && isnilinter(n->type) && + (n->left->type->width == widthptr) && + isint[simsimtype(n->left->type)]) { + l = nod(OEFACE, typename(n->left->type), n->left); + l->type = n->type; + l->typecheck = n->typecheck; + n = l; + goto ret; + } + // Build name of function: convI2E etc. // Not all names are possible // (e.g., we'll never generate convE2E or convE2I). - walkexpr(&n->left, init); strcpy(buf, "conv"); p = buf+strlen(buf); if(isnilinter(n->left->type)) @@ -719,6 +794,60 @@ walkexpr(Node **np, NodeList **init) ll = list(ll, typename(n->left->type)); if(!isnilinter(n->type)) ll = list(ll, typename(n->type)); + if(!isinter(n->left->type) && !isnilinter(n->type)){ + sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg); + if(sym->def == N) { + l = nod(ONAME, N, N); + l->sym = sym; + l->type = ptrto(types[TUINT8]); + l->addable = 1; + l->class = PEXTERN; + l->xoffset = 0; + sym->def = l; + ggloblsym(sym, widthptr, 1, 0); + } + l = nod(OADDR, sym->def, N); + l->addable = 1; + ll = list(ll, l); + + if(n->left->type->width == widthptr && + isint[simsimtype(n->left->type)]) { + /* For pointer types, we can make a special form of optimization + * + * These statements are put onto the expression init list: + * Itab *tab = atomicloadtype(&cache); + * if(tab == nil) + * tab = typ2Itab(type, itype, &cache); + * + * The CONVIFACE expression is replaced with this: + * OEFACE{tab, ptr}; + */ + l = temp(ptrto(types[TUINT8])); + + n1 = nod(OAS, l, sym->def); + typecheck(&n1, Etop); + *init = list(*init, n1); + + fn = syslook("typ2Itab", 1); + n1 = nod(OCALL, fn, N); + n1->list = ll; + typecheck(&n1, Erv); + walkexpr(&n1, init); + + n2 = nod(OIF, N, N); + n2->ntest = nod(OEQ, l, nodnil()); + n2->nbody = list1(nod(OAS, l, n1)); + n2->likely = -1; + typecheck(&n2, Etop); + *init = list(*init, n2); + + l = nod(OEFACE, l, n->left); + l->typecheck = n->typecheck; + l->type = n->type; + n = l; + goto ret; + } + } ll = list(ll, n->left); argtype(fn, n->left->type); argtype(fn, n->type); @@ -772,7 +901,7 @@ walkexpr(Node **np, NodeList **init) * on 386, rewrite float ops into l = l op r. * everywhere, rewrite map ops into l = l op r. * everywhere, rewrite string += into l = l op r. - * everywhere, rewrite complex /= into l = l op r. + * everywhere, rewrite integer/complex /= into l = l op r. * TODO(rsc): Maybe this rewrite should be done always? */ et = n->left->type->etype; @@ -780,7 +909,8 @@ walkexpr(Node **np, NodeList **init) (thechar == '8' && isfloat[et]) || l->op == OINDEXMAP || et == TSTRING || - (iscomplex[et] && n->etype == ODIV)) { + (!isfloat[et] && n->etype == ODIV) || + n->etype == OMOD) { l = safeexpr(n->left, init); a = l; if(a->op == OINDEXMAP) { @@ -794,15 +924,24 @@ walkexpr(Node **np, NodeList **init) typecheck(&r, Etop); walkexpr(&r, init); n = r; + goto ret; } + if(n->etype == OLSH || n->etype == ORSH) + goto shiftwalked; goto ret; case OANDNOT: walkexpr(&n->left, init); - walkexpr(&n->right, init); n->op = OAND; n->right = nod(OCOM, n->right, N); typecheck(&n->right, Erv); + walkexpr(&n->right, init); + goto ret; + + case OMUL: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + walkmul(&n, init); goto ret; case ODIV: @@ -821,63 +960,80 @@ walkexpr(Node **np, NodeList **init) n = conv(n, t); goto ret; } + // Nothing to do for float divisions. + if(isfloat[et]) + goto ret; + + // Try rewriting as shifts or magic multiplies. + walkdiv(&n, init); + /* - * rewrite div and mod into function calls + * rewrite 64-bit div and mod into function calls * on 32-bit architectures. */ - if(widthptr > 4 || (et != TUINT64 && et != TINT64)) - goto ret; - if(et == TINT64) - strcpy(namebuf, "int64"); - else - strcpy(namebuf, "uint64"); - if(n->op == ODIV) - strcat(namebuf, "div"); - else - strcat(namebuf, "mod"); - n = mkcall(namebuf, n->type, init, - conv(n->left, types[et]), conv(n->right, types[et])); + switch(n->op) { + case OMOD: + case ODIV: + if(widthptr > 4 || (et != TUINT64 && et != TINT64)) + goto ret; + if(et == TINT64) + strcpy(namebuf, "int64"); + else + strcpy(namebuf, "uint64"); + if(n->op == ODIV) + strcat(namebuf, "div"); + else + strcat(namebuf, "mod"); + n = mkcall(namebuf, n->type, init, + conv(n->left, types[et]), conv(n->right, types[et])); + break; + default: + break; + } goto ret; case OINDEX: walkexpr(&n->left, init); + // save the original node for bounds checking elision. + // If it was a ODIV/OMOD walk might rewrite it. + r = n->right; walkexpr(&n->right, init); // if range of type cannot exceed static array bound, - // disable bounds check - if(isfixedarray(n->left->type)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->type->bound) - n->etype = 1; - - if(isconst(n->left, CTSTR)) - if(!issigned[n->right->type->etype]) - if(n->right->type->width < 4) - if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) - n->etype = 1; - - // check for static out of bounds - if(isconst(n->right, CTINT) && !n->etype) { - v = mpgetfix(n->right->val.u.xval); - len = 1LL<<60; - t = n->left->type; - if(isconst(n->left, CTSTR)) - len = n->left->val.u.sval->len; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - if(v < 0 || v >= (1LL<<31) || v >= len) + // disable bounds check. + if(n->bounded) + goto ret; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + n->bounded = bounded(r, t->bound); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right) && !n->bounded) yyerror("index out of bounds"); - else if(isconst(n->left, CTSTR)) { - // replace "abc"[2] with 'b'. - // delayed until now because "abc"[2] is not - // an ideal constant. - nodconst(n, n->type, n->left->val.u.sval->s[v]); - n->typecheck = 1; + } else if(isconst(n->left, CTSTR)) { + n->bounded = bounded(r, n->left->val.u.sval->len); + if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) + warn("index bounds check elided"); + if(smallintconst(n->right)) { + if(!n->bounded) + yyerror("index out of bounds"); + else { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + v = mpgetfix(n->right->val.u.xval); + nodconst(n, n->type, n->left->val.u.sval->s[v]); + n->typecheck = 1; + } } } + + if(isconst(n->right, CTINT)) + if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 || + mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) + yyerror("index out of bounds"); goto ret; case OINDEXMAP: @@ -895,95 +1051,29 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSLICE: + if(n->right != N && n->right->left == N && n->right->right == N) { // noop + walkexpr(&n->left, init); + n = n->left; + goto ret; + } + // fallthrough case OSLICEARR: + case OSLICESTR: + if(n->right == N) // already processed + goto ret; + walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); + // cgen_slice can't handle string literals as source + // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] + if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX)) + n->left = copyexpr(n->left, n->left->type, init); + else + n->left = safeexpr(n->left, init); walkexpr(&n->right->left, init); n->right->left = safeexpr(n->right->left, init); walkexpr(&n->right->right, init); n->right->right = safeexpr(n->right->right, init); - - len = 1LL<<60; - t = n->left->type; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) - len = t->bound; - - // check for static out of bounds - // NOTE: v > len not v >= len. - v1 = -1; - v2 = -1; - if(isconst(n->right->left, CTINT)) { - v1 = mpgetfix(n->right->left->val.u.xval); - if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { - yyerror("slice index out of bounds"); - v1 = -1; - } - } - if(isconst(n->right->right, CTINT)) { - v2 = mpgetfix(n->right->right->val.u.xval); - if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { - yyerror("slice index out of bounds"); - v2 = -1; - } - } - if(v1 >= 0 && v2 >= 0 && v1 > v2) - yyerror("inverted slice range"); - - if(n->op == OSLICEARR) - goto slicearray; - - // dynamic slice - // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) - // sliceslice1(old []any, lb uint64, width uint64) (ary []any) - t = n->type; - et = n->etype; - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right != N) { - fn = syslook("sliceslice", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - conv(n->right->right, types[TUINT64]), - nodintconst(t->type->width)); - } else { - fn = syslook("sliceslice1", 1); - argtype(fn, t->type); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, t, init, - n->left, - l, - nodintconst(t->type->width)); - } - n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. - goto ret; - - slicearray: - // static slice - // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) - t = n->type; - fn = syslook("slicearray", 1); - argtype(fn, n->left->type->type); // any-1 - argtype(fn, t->type); // any-2 - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TUINT64]); - if(n->right->right == N) - r = nodintconst(n->left->type->type->bound); - else - r = conv(n->right->right, types[TUINT64]); - n = mkcall1(fn, t, init, - n->left, nodintconst(n->left->type->type->bound), - l, - r, - nodintconst(t->type->width)); + n = sliceany(n, init); // chops n->right, sets n->list goto ret; case OADDR: @@ -1031,27 +1121,34 @@ walkexpr(Node **np, NodeList **init) goto ret; } - // prepare for rewrite below if(n->etype == OEQ || n->etype == ONE) { + // prepare for rewrite below n->left = cheapexpr(n->left, init); n->right = cheapexpr(n->right, init); - } - // sys_cmpstring(s1, s2) :: 0 - r = mkcall("cmpstring", types[TINT], init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); - r = nod(n->etype, r, nodintconst(0)); + r = mkcall("eqstring", types[TBOOL], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); - // quick check of len before full compare for == or != - if(n->etype == OEQ || n->etype == ONE) { - if(n->etype == OEQ) + // quick check of len before full compare for == or != + if(n->etype == OEQ) { + // len(left) == len(right) && eqstring(left, right) r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); - else + } else { + // len(left) != len(right) || !eqstring(left, right) + r = nod(ONOT, r, N); r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + } typecheck(&r, Erv); walkexpr(&r, nil); + } else { + // sys_cmpstring(s1, s2) :: 0 + r = mkcall("cmpstring", types[TINT], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); + r = nod(n->etype, r, nodintconst(0)); } + typecheck(&r, Erv); if(n->type->etype != TBOOL) fatal("cmp %T", n->type); r->type = n->type; @@ -1061,25 +1158,7 @@ walkexpr(Node **np, NodeList **init) case OADDSTR: n = addstr(n, init); goto ret; - - case OSLICESTR: - // sys_slicestring(s, lb, hb) - if(n->right->left == N) - l = nodintconst(0); - else - l = conv(n->right->left, types[TINT]); - if(n->right->right) { - n = mkcall("slicestring", n->type, init, - conv(n->left, types[TSTRING]), - l, - conv(n->right->right, types[TINT])); - } else { - n = mkcall("slicestring1", n->type, init, - conv(n->left, types[TSTRING]), - l); - } - goto ret; - + case OAPPEND: if(n->isddd) { if(istype(n->type->type, TUINT8) && istype(n->list->next->n->type, TSTRING)) @@ -1177,6 +1256,9 @@ walkexpr(Node **np, NodeList **init) fn = syslook("efaceeq", 1); else fn = syslook("ifaceeq", 1); + + n->right = cheapexpr(n->right, init); + n->left = cheapexpr(n->left, init); argtype(fn, n->right->type); argtype(fn, n->left->type); r = mkcall1(fn, n->type, init, n->left, n->right); @@ -1189,7 +1271,7 @@ walkexpr(Node **np, NodeList **init) else r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); typecheck(&r, Erv); - walkexpr(&r, nil); + walkexpr(&r, init); r->type = n->type; n = r; goto ret; @@ -1226,9 +1308,16 @@ ret: static Node* ascompatee1(int op, Node *l, Node *r, NodeList **init) { + Node *n; USED(op); + + // convas will turn map assigns into function calls, + // making it impossible for reorder3 to work. + n = nod(OAS, l, r); + if(l->op == OINDEXMAP) + return n; - return convas(nod(OAS, l, r), init); + return convas(n, init); } static NodeList* @@ -1249,12 +1338,16 @@ ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) lr->n = safeexpr(lr->n, init); nn = nil; - for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) + for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) { + // Do not generate 'x = x' during return. See issue 4014. + if(op == ORETURN && ll->n == lr->n) + continue; nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); + } // cannot happen: caller checked that lists had same length if(ll || lr) - yyerror("error in shape across %+H %O %+H", nl, op, nr); + yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name); return nn; } @@ -1826,13 +1919,14 @@ static int aliased(Node*, NodeList*, NodeList*); static NodeList* reorder3(NodeList *all) { - NodeList *list, *early; + NodeList *list, *early, *mapinit; Node *l; // If a needed expression may be affected by an // earlier assignment, make an early copy of that // expression and use the copy instead. early = nil; + mapinit = nil; for(list=all; list; list=list->next) { l = list->n->left; @@ -1856,8 +1950,11 @@ reorder3(NodeList *all) case ONAME: break; case OINDEX: + case OINDEXMAP: reorder3save(&l->left, all, list, &early); reorder3save(&l->right, all, list, &early); + if(l->op == OINDEXMAP) + list->n = convas(list->n, &mapinit); break; case OIND: case ODOTPTR: @@ -1868,6 +1965,7 @@ reorder3(NodeList *all) reorder3save(&list->n->right, all, list, &early); } + early = concat(mapinit, early); return concat(early, all); } @@ -2119,6 +2217,8 @@ paramstoheap(Type **argin, int out) nn = nil; for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { v = t->nname; + if(v && v->sym && v->sym->name[0] == '~') + v = N; if(v == N && out && hasdefer) { // Defer might stop a panic and show the // return values as they exist at the time of panic. @@ -2395,12 +2495,12 @@ append(Node *n, NodeList **init) l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] for (a = n->list->next; a != nil; a = a->next) { nx = nod(OINDEX, ns, nn); // s[n] ... - nx->etype = 1; // disable bounds check + nx->bounded = 1; l = list(l, nod(OAS, nx, a->n)); // s[n] = arg if (a->next != nil) l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 @@ -2412,6 +2512,151 @@ append(Node *n, NodeList **init) return ns; } + +// Generate frontend part for OSLICE[ARR|STR] +// +static Node* +sliceany(Node* n, NodeList **init) +{ + int bounded; + Node *src, *lb, *hb, *bound, *chk, *chk1, *chk2; + int64 lbv, hbv, bv, w; + Type *bt; + +// print("before sliceany: %+N\n", n); + + src = n->left; + lb = n->right->left; + hb = n->right->right; + + bounded = n->etype; + + if(n->op == OSLICESTR) + bound = nod(OLEN, src, N); + else + bound = nod(OCAP, src, N); + + typecheck(&bound, Erv); + walkexpr(&bound, init); // if src is an array, bound will be a const now. + + // static checks if possible + bv = 1LL<<50; + if(isconst(bound, CTINT)) { + if(!smallintconst(bound)) + yyerror("array len too large"); + else + bv = mpgetfix(bound->val.u.xval); + } + + if(isconst(hb, CTINT)) { + hbv = mpgetfix(hb->val.u.xval); + if(hbv < 0 || hbv > bv) { + yyerror("slice index out of bounds"); + hbv = -1; + } + } + if(isconst(lb, CTINT)) { + lbv = mpgetfix(lb->val.u.xval); + if(lbv < 0 || lbv > bv) { + yyerror("slice index out of bounds"); + lbv = -1; + } + if(lbv == 0) + lb = N; + } + + // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison + // generate + // if hb > bound || lb > hb { panicslice() } + chk = N; + chk1 = N; + chk2 = N; + + bt = types[simtype[TUINT]]; + if(hb != N && hb->type->width > 4) + bt = types[TUINT64]; + if(lb != N && lb->type->width > 4) + bt = types[TUINT64]; + + bound = cheapexpr(conv(bound, bt), init); + + if(hb != N) { + hb = cheapexpr(conv(hb, bt), init); + if(!bounded) + chk1 = nod(OLT, bound, hb); + } else if(n->op == OSLICEARR) { + hb = bound; + } else { + hb = nod(OLEN, src, N); + typecheck(&hb, Erv); + walkexpr(&hb, init); + hb = cheapexpr(conv(hb, bt), init); + } + + if(lb != N) { + lb = cheapexpr(conv(lb, bt), init); + if(!bounded) + chk2 = nod(OLT, hb, lb); + } + + if(chk1 != N || chk2 != N) { + chk = nod(OIF, N, N); + chk->nbody = list1(mkcall("panicslice", T, init)); + if(chk1 != N) + chk->ntest = chk1; + if(chk2 != N) { + if(chk->ntest == N) + chk->ntest = chk2; + else + chk->ntest = nod(OOROR, chk->ntest, chk2); + } + typecheck(&chk, Etop); + walkstmt(&chk); + *init = concat(*init, chk->ninit); + chk->ninit = nil; + *init = list(*init, chk); + } + + // prepare new cap, len and offs for backend cgen_slice + // cap = bound [ - lo ] + n->right = N; + n->list = nil; + if(lb == N) + bound = conv(bound, types[simtype[TUINT]]); + else + bound = nod(OSUB, conv(bound, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&bound, Erv); + walkexpr(&bound, init); + n->list = list(n->list, bound); + + // len = hi [ - lo] + if(lb == N) + hb = conv(hb, types[simtype[TUINT]]); + else + hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); + typecheck(&hb, Erv); + walkexpr(&hb, init); + n->list = list(n->list, hb); + + // offs = [width *] lo, but omit if zero + if(lb != N) { + if(n->op == OSLICESTR) + w = 1; + else + w = n->type->type->width; + lb = conv(lb, types[TUINTPTR]); + if(w > 1) + lb = nod(OMUL, nodintconst(w), lb); + typecheck(&lb, Erv); + walkexpr(&lb, init); + n->list = list(n->list, lb); + } + +// print("after sliceany: %+N\n", n); + + return n; +} + static Node* eqfor(Type *t) { @@ -2531,6 +2776,8 @@ walkcompare(Node **np, NodeList **init) // Struct of four or fewer fields. // Inline comparisons. for(t1=t->type; t1; t1=t1->down) { + if(isblanksym(t1->sym)) + continue; li = nod(OXDOT, l, newname(t1->sym)); ri = nod(OXDOT, r, newname(t1->sym)); a = nod(n->op, li, ri); @@ -2596,3 +2843,591 @@ hard: *np = r; return; } + +static int +samecheap(Node *a, Node *b) +{ + if(a == N || b == N || a->op != b->op) + return 0; + + switch(a->op) { + case ONAME: + return a == b; + // TODO: Could do more here, but maybe this is enough. + // It's all cheapexpr does. + } + return 0; +} + +static void +walkrotate(Node **np) +{ + int w, sl, sr, s; + Node *l, *r; + Node *n; + + n = *np; + + // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. + l = n->left; + r = n->right; + if((n->op != OOR && n->op != OXOR) || + (l->op != OLSH && l->op != ORSH) || + (r->op != OLSH && r->op != ORSH) || + n->type == T || issigned[n->type->etype] || + l->op == r->op) { + return; + } + + // Want same, side effect-free expression on lhs of both shifts. + if(!samecheap(l->left, r->left)) + return; + + // Constants adding to width? + w = l->type->width * 8; + if(smallintconst(l->right) && smallintconst(r->right)) { + if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w) + goto yes; + return; + } + + // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). + return; + +yes: + // Rewrite left shift half to left rotate. + if(l->op == OLSH) + n = l; + else + n = r; + n->op = OLROT; + + // Remove rotate 0 and rotate w. + s = mpgetfix(n->right->val.u.xval); + if(s == 0 || s == w) + n = n->left; + + *np = n; + return; +} + +/* + * walkmul rewrites integer multiplication by powers of two as shifts. + */ +static void +walkmul(Node **np, NodeList **init) +{ + Node *n, *nl, *nr; + int pow, neg, w; + + n = *np; + if(!isint[n->type->etype]) + return; + + if(n->right->op == OLITERAL) { + nl = n->left; + nr = n->right; + } else if(n->left->op == OLITERAL) { + nl = n->right; + nr = n->left; + } else + return; + + neg = 0; + + // x*0 is 0 (and side effects of x). + if(mpgetfix(nr->val.u.xval) == 0) { + cheapexpr(nl, init); + nodconst(n, n->type, 0); + goto ret; + } + + // nr is a constant. + pow = powtwo(nr); + if(pow < 0) + return; + if(pow >= 1000) { + // negative power of 2, like -16 + neg = 1; + pow -= 1000; + } + + w = nl->type->width*8; + if(pow+1 >= w)// too big, shouldn't happen + return; + + nl = cheapexpr(nl, init); + + if(pow == 0) { + // x*1 is x + n = nl; + goto ret; + } + + n = nod(OLSH, nl, nodintconst(pow)); + +ret: + if(neg) + n = nod(OMINUS, n, N); + + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +/* + * walkdiv rewrites division by a constant as less expensive + * operations. + */ +static void +walkdiv(Node **np, NodeList **init) +{ + Node *n, *nl, *nr, *nc; + Node *n1, *n2, *n3, *n4; + int pow; // if >= 0, nr is 1<<pow + int s; // 1 if nr is negative. + int w; + Type *twide; + Magic m; + + n = *np; + if(n->right->op != OLITERAL) + return; + // nr is a constant. + nl = cheapexpr(n->left, init); + nr = n->right; + + // special cases of mod/div + // by a constant + w = nl->type->width*8; + s = 0; + pow = powtwo(nr); + if(pow >= 1000) { + // negative power of 2 + s = 1; + pow -= 1000; + } + + if(pow+1 >= w) { + // divisor too large. + return; + } + if(pow < 0) { + goto divbymul; + } + + switch(pow) { + case 0: + if(n->op == OMOD) { + // nl % 1 is zero. + nodconst(n, n->type, 0); + } else if(s) { + // divide by -1 + n->op = OMINUS; + n->right = N; + } else { + // divide by 1 + n = nl; + } + break; + default: + if(issigned[n->type->etype]) { + if(n->op == OMOD) { + // signed modulo 2^pow is like ANDing + // with the last pow bits, but if nl < 0, + // nl & (2^pow-1) is (nl+1)%2^pow - 1. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + typecheck(&n1, Erv); + n1 = cheapexpr(n1, init); + // n = (nl+ε)&1 -ε where ε=1 iff nl<0. + n2 = nod(OSUB, nl, n1); + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, 1); + n3 = nod(OAND, n2, nc); + n = nod(OADD, n3, n1); + } else { + // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, (1LL<<pow)-1); + n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0. + typecheck(&n2, Erv); + n2 = cheapexpr(n2, init); + + n3 = nod(OADD, nl, n2); + n4 = nod(OAND, n3, nc); + n = nod(OSUB, n4, n2); + } + break; + } else { + // arithmetic right shift does not give the correct rounding. + // if nl >= 0, nl >> n == nl / nr + // if nl < 0, we want to add 2^n-1 first. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-1); + n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. + if(pow == 1) { + // nl+1 is nl-(-1) + n->left = nod(OSUB, nl, n1); + } else { + // Do a logical right right on -1 to keep pow bits. + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], w-pow); + n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc); + n->left = nod(OADD, nl, conv(n2, nl->type)); + } + // n = (nl + 2^pow-1) >> pow + n->op = ORSH; + nc = nod(OXXX, N, N); + nodconst(nc, types[simtype[TUINT]], pow); + n->right = nc; + n->typecheck = 0; + } + if(s) + n = nod(OMINUS, n, N); + break; + } + nc = nod(OXXX, N, N); + if(n->op == OMOD) { + // n = nl & (nr-1) + n->op = OAND; + nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1); + } else { + // n = nl >> pow + n->op = ORSH; + nodconst(nc, types[simtype[TUINT]], pow); + } + n->typecheck = 0; + n->right = nc; + break; + } + goto ret; + +divbymul: + // try to do division by multiply by (2^w)/d + // see hacker's delight chapter 10 + // TODO: support 64-bit magic multiply here. + m.w = w; + if(issigned[nl->type->etype]) { + m.sd = mpgetfix(nr->val.u.xval); + smagic(&m); + } else { + m.ud = mpgetfix(nr->val.u.xval); + umagic(&m); + } + if(m.bad) + return; + + // We have a quick division method so use it + // for modulo too. + if(n->op == OMOD) + goto longmod; + + switch(simtype[nl->type->etype]) { + default: + return; + + case TUINT8: + case TUINT16: + case TUINT32: + // n1 = nl * magic >> w (HMUL) + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.um); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.ua) { + // Select a Go type with (at least) twice the width. + switch(simtype[nl->type->etype]) { + default: + return; + case TUINT8: + case TUINT16: + twide = types[TUINT32]; + break; + case TUINT32: + twide = types[TUINT64]; + break; + case TINT8: + case TINT16: + twide = types[TINT32]; + break; + case TINT32: + twide = types[TINT64]; + break; + } + + // add numerator (might overflow). + // n2 = (n1 + nl) + n2 = nod(OADD, conv(n1, twide), conv(nl, twide)); + + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = conv(nod(ORSH, n2, nc), nl->type); + } else { + // n = n1 >> m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n = nod(ORSH, n1, nc); + } + break; + + case TINT8: + case TINT16: + case TINT32: + // n1 = nl * magic >> w + nc = nod(OXXX, N, N); + nodconst(nc, nl->type, m.sm); + n1 = nod(OMUL, nl, nc); + typecheck(&n1, Erv); + n1->op = OHMUL; + if(m.sm < 0) { + // add the numerator. + n1 = nod(OADD, n1, nl); + } + // shift by m.s + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], m.s); + n2 = conv(nod(ORSH, n1, nc), nl->type); + // add 1 iff n1 is negative. + nc = nod(OXXX, N, N); + nodconst(nc, types[TUINT], w-1); + n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative. + n = nod(OSUB, n2, n3); + // apply sign. + if(m.sd < 0) + n = nod(OMINUS, n, N); + break; + } + goto ret; + +longmod: + // rewrite as A%B = A - (A/B*B). + n1 = nod(ODIV, nl, nr); + n2 = nod(OMUL, n1, nr); + n = nod(OSUB, nl, n2); + goto ret; + +ret: + typecheck(&n, Erv); + walkexpr(&n, init); + *np = n; +} + +// return 1 if integer n must be in range [0, max), 0 otherwise +static int +bounded(Node *n, int64 max) +{ + int64 v; + int32 bits; + int sign; + + if(n->type == T || !isint[n->type->etype]) + return 0; + + sign = issigned[n->type->etype]; + bits = 8*n->type->width; + + if(smallintconst(n)) { + v = mpgetfix(n->val.u.xval); + return 0 <= v && v < max; + } + + switch(n->op) { + case OAND: + v = -1; + if(smallintconst(n->left)) { + v = mpgetfix(n->left->val.u.xval); + } else if(smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + } + if(0 <= v && v < max) + return 1; + break; + + case OMOD: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(0 <= v && v <= max) + return 1; + } + break; + + case ODIV: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + while(bits > 0 && v >= 2) { + bits--; + v >>= 1; + } + } + break; + + case ORSH: + if(!sign && smallintconst(n->right)) { + v = mpgetfix(n->right->val.u.xval); + if(v > bits) + return 1; + bits -= v; + } + break; + } + + if(!sign && bits <= 62 && (1LL<<bits) <= max) + return 1; + + return 0; +} + +void +usefield(Node *n) +{ + Type *field, *l; + + if(!fieldtrack_enabled) + return; + + switch(n->op) { + default: + fatal("usefield %O", n->op); + case ODOT: + case ODOTPTR: + break; + } + + field = n->paramfld; + if(field == T) + fatal("usefield %T %S without paramfld", n->left->type, n->right->sym); + if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil) + return; + + // dedup on list + if(field->lastfn == curfn) + return; + field->lastfn = curfn; + field->outer = n->left->type; + if(isptr[field->outer->etype]) + field->outer = field->outer->type; + if(field->outer->sym == S) + yyerror("tracked field must be in named struct type"); + if(!exportname(field->sym->name)) + yyerror("tracked field must be exported (upper case)"); + + l = typ(0); + l->type = field; + l->down = curfn->paramfld; + curfn->paramfld = l; +} + +static int +candiscardlist(NodeList *l) +{ + for(; l; l=l->next) + if(!candiscard(l->n)) + return 0; + return 1; +} + +int +candiscard(Node *n) +{ + if(n == N) + return 1; + + switch(n->op) { + default: + return 0; + + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + case OADD: + case OSUB: + case OOR: + case OXOR: + case OADDSTR: + case OADDR: + case OANDAND: + case OARRAYBYTESTR: + case OARRAYRUNESTR: + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + case OCAP: + case OCMPIFACE: + case OCMPSTR: + case OCOMPLIT: + case OMAPLIT: + case OSTRUCTLIT: + case OARRAYLIT: + case OPTRLIT: + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case ODOT: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGT: + case OGE: + case OKEY: + case OLEN: + case OMUL: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case ONEW: + case ONOT: + case OCOM: + case OPLUS: + case OMINUS: + case OOROR: + case OPAREN: + case ORUNESTR: + case OREAL: + case OIMAG: + case OCOMPLEX: + // Discardable as long as the subpieces are. + break; + + case ODIV: + case OMOD: + // Discardable as long as we know it's not division by zero. + if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0) + break; + if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0) + break; + return 0; + + case OMAKECHAN: + case OMAKEMAP: + // Discardable as long as we know it won't fail because of a bad size. + if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0) + break; + return 0; + + case OMAKESLICE: + // Difficult to tell what sizes are okay. + return 0; + } + + if(!candiscard(n->left) || + !candiscard(n->right) || + !candiscard(n->ntest) || + !candiscard(n->nincr) || + !candiscardlist(n->ninit) || + !candiscardlist(n->nbody) || + !candiscardlist(n->nelse) || + !candiscardlist(n->list) || + !candiscardlist(n->rlist)) { + return 0; + } + + return 1; +} diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 97bf233eb..75175455e 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - + the Free Software Foundation; either version 2, or (at your option) + any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -26,7 +29,7 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ @@ -44,7 +47,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.5" +#define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -52,52 +55,11 @@ /* Pure parsers. */ #define YYPURE 0 -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - /* Using locations. */ #define YYLSP_NEEDED 0 -/* Copy the first part of user declarations. */ - -/* Line 268 of yacc.c */ -#line 20 "go.y" - -#include <u.h> -#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ -#include <libc.h> -#include "go.h" - -static void fixlbrace(int); - - -/* Line 268 of yacc.c */ -#line 81 "y.tab.c" - -/* Enabling traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 1 -#endif - -/* Enabling the token table. */ -#ifndef YYTOKEN_TABLE -# define YYTOKEN_TABLE 0 -#endif - - /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -106,10 +68,10 @@ static void fixlbrace(int); enum yytokentype { LLITERAL = 258, LASOP = 259, - LBREAK = 260, - LCASE = 261, - LCHAN = 262, - LCOLAS = 263, + LCOLAS = 260, + LBREAK = 261, + LCASE = 262, + LCHAN = 263, LCONST = 264, LCONTINUE = 265, LDDD = 266, @@ -158,10 +120,10 @@ static void fixlbrace(int); /* Tokens. */ #define LLITERAL 258 #define LASOP 259 -#define LBREAK 260 -#define LCASE 261 -#define LCHAN 262 -#define LCOLAS 263 +#define LCOLAS 260 +#define LBREAK 261 +#define LCASE 262 +#define LCHAN 263 #define LCONST 264 #define LCONTINUE 265 #define LDDD 266 @@ -209,36 +171,61 @@ static void fixlbrace(int); +/* Copy the first part of user declarations. */ +#line 20 "go.y" + +#include <u.h> +#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ +#include <libc.h> +#include "go.h" + +static void fixlbrace(int); + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - -/* Line 293 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 293 of yacc.c */ -#line 230 "y.tab.c" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* Line 193 of yacc.c. */ +#line 216 "y.tab.c" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 #endif + /* Copy the second part of user declarations. */ -/* Line 343 of yacc.c */ -#line 242 "y.tab.c" +/* Line 216 of yacc.c. */ +#line 229 "y.tab.c" #ifdef short # undef short @@ -313,14 +300,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int yyi) +YYID (int i) #else static int -YYID (yyi) - int yyi; +YYID (i) + int i; #endif { - return yyi; + return i; } #endif @@ -341,11 +328,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # endif @@ -368,24 +355,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ +# if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif @@ -401,9 +388,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; -}; + yytype_int16 yyss; + YYSTYPE yyvs; + }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) @@ -414,27 +401,6 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -452,21 +418,38 @@ union yyalloc while (YYID (0)) # endif # endif -#endif /* !YYCOPY_NEEDED */ + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 4 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 2131 +#define YYLAST 2194 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 76 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 138 +#define YYNNTS 142 /* YYNRULES -- Number of rules. */ -#define YYNRULES 344 +#define YYNRULES 349 /* YYNRULES -- Number of states. */ -#define YYNSTATES 653 +#define YYNSTATES 663 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -524,148 +507,150 @@ static const yytype_uint16 yyprhs[] = 162, 164, 168, 172, 176, 179, 182, 186, 192, 198, 201, 202, 207, 208, 212, 213, 216, 217, 222, 227, 232, 238, 240, 242, 245, 246, 250, 252, 256, 257, - 258, 259, 267, 268, 271, 274, 275, 276, 284, 285, - 291, 293, 297, 301, 305, 309, 313, 317, 321, 325, - 329, 333, 337, 341, 345, 349, 353, 357, 361, 365, - 369, 373, 375, 378, 381, 384, 387, 390, 393, 396, - 399, 403, 409, 416, 418, 420, 424, 430, 436, 441, - 448, 450, 455, 461, 467, 475, 477, 478, 482, 484, - 489, 491, 495, 497, 499, 501, 503, 505, 507, 509, - 510, 512, 514, 516, 518, 523, 525, 527, 529, 532, - 534, 536, 538, 540, 542, 546, 548, 550, 552, 555, - 557, 559, 561, 563, 567, 569, 571, 573, 575, 577, - 579, 581, 583, 585, 589, 594, 599, 602, 606, 612, - 614, 616, 619, 623, 629, 633, 639, 643, 647, 653, - 662, 668, 677, 683, 684, 688, 689, 691, 695, 697, - 702, 705, 706, 710, 712, 716, 718, 722, 724, 728, - 730, 734, 736, 740, 744, 747, 752, 756, 762, 768, - 770, 774, 776, 779, 781, 785, 790, 792, 795, 798, - 800, 802, 806, 807, 810, 811, 813, 815, 817, 819, - 821, 823, 825, 827, 829, 830, 835, 837, 840, 843, - 846, 849, 852, 855, 857, 861, 863, 867, 869, 873, - 875, 879, 881, 885, 887, 889, 893, 897, 898, 901, - 902, 904, 905, 907, 908, 910, 911, 913, 914, 916, - 917, 919, 920, 922, 923, 925, 926, 928, 933, 938, - 944, 951, 956, 961, 963, 965, 967, 969, 971, 973, - 975, 977, 979, 983, 988, 994, 999, 1004, 1007, 1010, - 1015, 1019, 1023, 1029, 1033, 1038, 1042, 1048, 1050, 1051, - 1053, 1057, 1059, 1061, 1064, 1066, 1068, 1074, 1075, 1078, - 1080, 1084, 1086, 1090, 1092 + 258, 259, 268, 269, 275, 276, 279, 280, 283, 284, + 285, 293, 294, 300, 302, 306, 310, 314, 318, 322, + 326, 330, 334, 338, 342, 346, 350, 354, 358, 362, + 366, 370, 374, 378, 382, 384, 387, 390, 393, 396, + 399, 402, 405, 408, 412, 418, 425, 427, 429, 433, + 439, 445, 450, 457, 459, 465, 471, 477, 485, 487, + 488, 492, 494, 499, 501, 506, 508, 512, 514, 516, + 518, 520, 522, 524, 526, 527, 529, 531, 533, 535, + 540, 542, 544, 546, 549, 551, 553, 555, 557, 559, + 563, 565, 567, 569, 572, 574, 576, 578, 580, 584, + 586, 588, 590, 592, 594, 596, 598, 600, 602, 606, + 611, 616, 619, 623, 629, 631, 633, 636, 640, 646, + 650, 656, 660, 664, 670, 679, 685, 694, 700, 701, + 705, 706, 708, 712, 714, 719, 722, 723, 727, 729, + 733, 735, 739, 741, 745, 747, 751, 753, 757, 761, + 764, 769, 773, 779, 785, 787, 791, 793, 796, 798, + 802, 807, 809, 812, 815, 817, 819, 823, 824, 827, + 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, + 847, 852, 854, 857, 860, 863, 866, 869, 872, 874, + 878, 880, 884, 886, 890, 892, 896, 898, 902, 904, + 906, 910, 914, 915, 918, 919, 921, 922, 924, 925, + 927, 928, 930, 931, 933, 934, 936, 937, 939, 940, + 942, 943, 945, 950, 955, 961, 968, 973, 978, 980, + 982, 984, 986, 988, 990, 992, 994, 996, 1000, 1005, + 1011, 1016, 1021, 1024, 1027, 1032, 1036, 1040, 1046, 1050, + 1055, 1059, 1065, 1067, 1068, 1070, 1074, 1076, 1078, 1081, + 1083, 1085, 1091, 1092, 1095, 1097, 1101, 1103, 1107, 1109 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { - 77, 0, -1, 79, 78, 81, 162, -1, -1, 25, - 137, 62, -1, -1, 80, 86, 88, -1, -1, 81, - 82, 62, -1, 21, 83, -1, 21, 59, 84, 186, + 77, 0, -1, 79, 78, 81, 166, -1, -1, 25, + 141, 62, -1, -1, 80, 86, 88, -1, -1, 81, + 82, 62, -1, 21, 83, -1, 21, 59, 84, 190, 60, -1, 21, 59, 60, -1, 85, 86, 88, -1, 85, 88, -1, 83, -1, 84, 62, 83, -1, 3, - -1, 137, 3, -1, 63, 3, -1, 25, 24, 87, - 62, -1, -1, 24, -1, -1, 89, 210, 64, 64, - -1, -1, 91, -1, 154, -1, 177, -1, 1, -1, - 32, 93, -1, 32, 59, 163, 186, 60, -1, 32, - 59, 60, -1, 92, 94, -1, 92, 59, 94, 186, - 60, -1, 92, 59, 94, 62, 164, 186, 60, -1, - 92, 59, 60, -1, 31, 97, -1, 31, 59, 165, - 186, 60, -1, 31, 59, 60, -1, 9, -1, 181, - 142, -1, 181, 142, 65, 182, -1, 181, 65, 182, - -1, 181, 142, 65, 182, -1, 181, 65, 182, -1, - 94, -1, 181, 142, -1, 181, -1, 137, -1, 96, - 142, -1, 123, -1, 123, 4, 123, -1, 182, 65, - 182, -1, 182, 8, 182, -1, 123, 42, -1, 123, - 37, -1, 6, 183, 66, -1, 6, 183, 65, 123, - 66, -1, 6, 183, 8, 123, 66, -1, 12, 66, - -1, -1, 67, 101, 179, 68, -1, -1, 99, 103, - 179, -1, -1, 104, 102, -1, -1, 35, 106, 179, - 68, -1, 182, 65, 26, 123, -1, 182, 8, 26, - 123, -1, 190, 62, 190, 62, 190, -1, 190, -1, + -1, 141, 3, -1, 63, 3, -1, 25, 24, 87, + 62, -1, -1, 24, -1, -1, 89, 214, 64, 64, + -1, -1, 91, -1, 158, -1, 181, -1, 1, -1, + 32, 93, -1, 32, 59, 167, 190, 60, -1, 32, + 59, 60, -1, 92, 94, -1, 92, 59, 94, 190, + 60, -1, 92, 59, 94, 62, 168, 190, 60, -1, + 92, 59, 60, -1, 31, 97, -1, 31, 59, 169, + 190, 60, -1, 31, 59, 60, -1, 9, -1, 185, + 146, -1, 185, 146, 65, 186, -1, 185, 65, 186, + -1, 185, 146, 65, 186, -1, 185, 65, 186, -1, + 94, -1, 185, 146, -1, 185, -1, 141, -1, 96, + 146, -1, 126, -1, 126, 4, 126, -1, 186, 65, + 186, -1, 186, 5, 186, -1, 126, 42, -1, 126, + 37, -1, 7, 187, 66, -1, 7, 187, 65, 126, + 66, -1, 7, 187, 5, 126, 66, -1, 12, 66, + -1, -1, 67, 101, 183, 68, -1, -1, 99, 103, + 183, -1, -1, 104, 102, -1, -1, 35, 106, 183, + 68, -1, 186, 65, 26, 126, -1, 186, 5, 26, + 126, -1, 194, 62, 194, 62, 194, -1, 194, -1, 107, -1, 108, 105, -1, -1, 16, 111, 109, -1, - 190, -1, 190, 62, 190, -1, -1, -1, -1, 20, - 114, 112, 115, 105, 116, 117, -1, -1, 14, 113, - -1, 14, 100, -1, -1, -1, 30, 119, 112, 120, - 35, 104, 68, -1, -1, 28, 122, 35, 104, 68, - -1, 124, -1, 123, 47, 123, -1, 123, 33, 123, - -1, 123, 38, 123, -1, 123, 46, 123, -1, 123, - 45, 123, -1, 123, 43, 123, -1, 123, 39, 123, - -1, 123, 40, 123, -1, 123, 49, 123, -1, 123, - 50, 123, -1, 123, 51, 123, -1, 123, 52, 123, - -1, 123, 53, 123, -1, 123, 54, 123, -1, 123, - 55, 123, -1, 123, 56, 123, -1, 123, 34, 123, - -1, 123, 44, 123, -1, 123, 48, 123, -1, 123, - 36, 123, -1, 130, -1, 53, 124, -1, 56, 124, - -1, 49, 124, -1, 50, 124, -1, 69, 124, -1, - 70, 124, -1, 52, 124, -1, 36, 124, -1, 130, - 59, 60, -1, 130, 59, 183, 187, 60, -1, 130, - 59, 183, 11, 187, 60, -1, 3, -1, 139, -1, - 130, 63, 137, -1, 130, 63, 59, 131, 60, -1, - 130, 63, 59, 31, 60, -1, 130, 71, 123, 72, - -1, 130, 71, 188, 66, 188, 72, -1, 125, -1, - 145, 59, 123, 60, -1, 146, 133, 127, 185, 68, - -1, 126, 67, 127, 185, 68, -1, 59, 131, 60, - 67, 127, 185, 68, -1, 161, -1, -1, 123, 66, - 129, -1, 123, -1, 67, 127, 185, 68, -1, 126, - -1, 59, 131, 60, -1, 123, -1, 143, -1, 142, - -1, 35, -1, 67, -1, 137, -1, 137, -1, -1, - 134, -1, 24, -1, 138, -1, 73, -1, 74, 3, - 63, 24, -1, 137, -1, 134, -1, 11, -1, 11, - 142, -1, 151, -1, 157, -1, 149, -1, 150, -1, - 148, -1, 59, 142, 60, -1, 151, -1, 157, -1, - 149, -1, 53, 143, -1, 157, -1, 149, -1, 150, - -1, 148, -1, 59, 142, 60, -1, 157, -1, 149, - -1, 149, -1, 151, -1, 157, -1, 149, -1, 150, - -1, 148, -1, 139, -1, 139, 63, 137, -1, 71, - 188, 72, 142, -1, 71, 11, 72, 142, -1, 7, - 144, -1, 7, 36, 142, -1, 23, 71, 142, 72, - 142, -1, 152, -1, 153, -1, 53, 142, -1, 36, - 7, 142, -1, 29, 133, 166, 186, 68, -1, 29, - 133, 68, -1, 22, 133, 167, 186, 68, -1, 22, - 133, 68, -1, 17, 155, 158, -1, 137, 59, 175, - 60, 159, -1, 59, 175, 60, 137, 59, 175, 60, - 159, -1, 196, 59, 191, 60, 206, -1, 59, 211, - 60, 137, 59, 191, 60, 206, -1, 17, 59, 175, - 60, 159, -1, -1, 67, 179, 68, -1, -1, 147, - -1, 59, 175, 60, -1, 157, -1, 160, 133, 179, - 68, -1, 160, 1, -1, -1, 162, 90, 62, -1, - 93, -1, 163, 62, 93, -1, 95, -1, 164, 62, - 95, -1, 97, -1, 165, 62, 97, -1, 168, -1, - 166, 62, 168, -1, 171, -1, 167, 62, 171, -1, - 180, 142, 194, -1, 170, 194, -1, 59, 170, 60, - 194, -1, 53, 170, 194, -1, 59, 53, 170, 60, - 194, -1, 53, 59, 170, 60, 194, -1, 24, -1, - 24, 63, 137, -1, 169, -1, 134, 172, -1, 169, - -1, 59, 169, 60, -1, 59, 175, 60, 159, -1, - 132, -1, 137, 132, -1, 137, 141, -1, 141, -1, - 173, -1, 174, 75, 173, -1, -1, 174, 187, -1, - -1, 100, -1, 91, -1, 177, -1, 1, -1, 98, - -1, 110, -1, 118, -1, 121, -1, 113, -1, -1, - 140, 66, 178, 176, -1, 15, -1, 5, 136, -1, - 10, 136, -1, 18, 125, -1, 13, 125, -1, 19, - 134, -1, 27, 189, -1, 176, -1, 179, 62, 176, - -1, 134, -1, 180, 75, 134, -1, 135, -1, 181, - 75, 135, -1, 123, -1, 182, 75, 123, -1, 131, - -1, 183, 75, 131, -1, 128, -1, 129, -1, 184, - 75, 128, -1, 184, 75, 129, -1, -1, 184, 187, - -1, -1, 62, -1, -1, 75, -1, -1, 123, -1, - -1, 182, -1, -1, 98, -1, -1, 211, -1, -1, - 212, -1, -1, 213, -1, -1, 3, -1, 21, 24, - 3, 62, -1, 32, 196, 198, 62, -1, 9, 196, - 65, 209, 62, -1, 9, 196, 198, 65, 209, 62, - -1, 31, 197, 198, 62, -1, 17, 156, 158, 62, - -1, 138, -1, 196, -1, 200, -1, 201, -1, 202, - -1, 200, -1, 202, -1, 138, -1, 24, -1, 71, - 72, 198, -1, 71, 3, 72, 198, -1, 23, 71, - 198, 72, 198, -1, 29, 67, 192, 68, -1, 22, - 67, 193, 68, -1, 53, 198, -1, 7, 199, -1, - 7, 59, 201, 60, -1, 7, 36, 198, -1, 36, - 7, 198, -1, 17, 59, 191, 60, 206, -1, 137, - 198, 194, -1, 137, 11, 198, 194, -1, 137, 198, - 194, -1, 137, 59, 191, 60, 206, -1, 198, -1, - -1, 207, -1, 59, 191, 60, -1, 198, -1, 3, - -1, 50, 3, -1, 137, -1, 208, -1, 59, 208, - 49, 208, 60, -1, -1, 210, 195, -1, 203, -1, - 211, 75, 203, -1, 204, -1, 212, 62, 204, -1, - 205, -1, 213, 62, 205, -1 + 194, -1, 194, 62, 194, -1, -1, -1, -1, 20, + 114, 112, 115, 105, 116, 119, 120, -1, -1, 14, + 20, 118, 112, 105, -1, -1, 119, 117, -1, -1, + 14, 100, -1, -1, -1, 30, 122, 112, 123, 35, + 104, 68, -1, -1, 28, 125, 35, 104, 68, -1, + 127, -1, 126, 47, 126, -1, 126, 33, 126, -1, + 126, 38, 126, -1, 126, 46, 126, -1, 126, 45, + 126, -1, 126, 43, 126, -1, 126, 39, 126, -1, + 126, 40, 126, -1, 126, 49, 126, -1, 126, 50, + 126, -1, 126, 51, 126, -1, 126, 52, 126, -1, + 126, 53, 126, -1, 126, 54, 126, -1, 126, 55, + 126, -1, 126, 56, 126, -1, 126, 34, 126, -1, + 126, 44, 126, -1, 126, 48, 126, -1, 126, 36, + 126, -1, 134, -1, 53, 127, -1, 56, 127, -1, + 49, 127, -1, 50, 127, -1, 69, 127, -1, 70, + 127, -1, 52, 127, -1, 36, 127, -1, 134, 59, + 60, -1, 134, 59, 187, 191, 60, -1, 134, 59, + 187, 11, 191, 60, -1, 3, -1, 143, -1, 134, + 63, 141, -1, 134, 63, 59, 135, 60, -1, 134, + 63, 59, 31, 60, -1, 134, 71, 126, 72, -1, + 134, 71, 192, 66, 192, 72, -1, 128, -1, 149, + 59, 126, 191, 60, -1, 150, 137, 130, 189, 68, + -1, 129, 67, 130, 189, 68, -1, 59, 135, 60, + 67, 130, 189, 68, -1, 165, -1, -1, 126, 66, + 133, -1, 126, -1, 67, 130, 189, 68, -1, 126, + -1, 67, 130, 189, 68, -1, 129, -1, 59, 135, + 60, -1, 126, -1, 147, -1, 146, -1, 35, -1, + 67, -1, 141, -1, 141, -1, -1, 138, -1, 24, + -1, 142, -1, 73, -1, 74, 3, 63, 24, -1, + 141, -1, 138, -1, 11, -1, 11, 146, -1, 155, + -1, 161, -1, 153, -1, 154, -1, 152, -1, 59, + 146, 60, -1, 155, -1, 161, -1, 153, -1, 53, + 147, -1, 161, -1, 153, -1, 154, -1, 152, -1, + 59, 146, 60, -1, 161, -1, 153, -1, 153, -1, + 155, -1, 161, -1, 153, -1, 154, -1, 152, -1, + 143, -1, 143, 63, 141, -1, 71, 192, 72, 146, + -1, 71, 11, 72, 146, -1, 8, 148, -1, 8, + 36, 146, -1, 23, 71, 146, 72, 146, -1, 156, + -1, 157, -1, 53, 146, -1, 36, 8, 146, -1, + 29, 137, 170, 190, 68, -1, 29, 137, 68, -1, + 22, 137, 171, 190, 68, -1, 22, 137, 68, -1, + 17, 159, 162, -1, 141, 59, 179, 60, 163, -1, + 59, 179, 60, 141, 59, 179, 60, 163, -1, 200, + 59, 195, 60, 210, -1, 59, 215, 60, 141, 59, + 195, 60, 210, -1, 17, 59, 179, 60, 163, -1, + -1, 67, 183, 68, -1, -1, 151, -1, 59, 179, + 60, -1, 161, -1, 164, 137, 183, 68, -1, 164, + 1, -1, -1, 166, 90, 62, -1, 93, -1, 167, + 62, 93, -1, 95, -1, 168, 62, 95, -1, 97, + -1, 169, 62, 97, -1, 172, -1, 170, 62, 172, + -1, 175, -1, 171, 62, 175, -1, 184, 146, 198, + -1, 174, 198, -1, 59, 174, 60, 198, -1, 53, + 174, 198, -1, 59, 53, 174, 60, 198, -1, 53, + 59, 174, 60, 198, -1, 24, -1, 24, 63, 141, + -1, 173, -1, 138, 176, -1, 173, -1, 59, 173, + 60, -1, 59, 179, 60, 163, -1, 136, -1, 141, + 136, -1, 141, 145, -1, 145, -1, 177, -1, 178, + 75, 177, -1, -1, 178, 191, -1, -1, 100, -1, + 91, -1, 181, -1, 1, -1, 98, -1, 110, -1, + 121, -1, 124, -1, 113, -1, -1, 144, 66, 182, + 180, -1, 15, -1, 6, 140, -1, 10, 140, -1, + 18, 128, -1, 13, 128, -1, 19, 138, -1, 27, + 193, -1, 180, -1, 183, 62, 180, -1, 138, -1, + 184, 75, 138, -1, 139, -1, 185, 75, 139, -1, + 126, -1, 186, 75, 126, -1, 135, -1, 187, 75, + 135, -1, 131, -1, 132, -1, 188, 75, 131, -1, + 188, 75, 132, -1, -1, 188, 191, -1, -1, 62, + -1, -1, 75, -1, -1, 126, -1, -1, 186, -1, + -1, 98, -1, -1, 215, -1, -1, 216, -1, -1, + 217, -1, -1, 3, -1, 21, 24, 3, 62, -1, + 32, 200, 202, 62, -1, 9, 200, 65, 213, 62, + -1, 9, 200, 202, 65, 213, 62, -1, 31, 201, + 202, 62, -1, 17, 160, 162, 62, -1, 142, -1, + 200, -1, 204, -1, 205, -1, 206, -1, 204, -1, + 206, -1, 142, -1, 24, -1, 71, 72, 202, -1, + 71, 3, 72, 202, -1, 23, 71, 202, 72, 202, + -1, 29, 67, 196, 68, -1, 22, 67, 197, 68, + -1, 53, 202, -1, 8, 203, -1, 8, 59, 205, + 60, -1, 8, 36, 202, -1, 36, 8, 202, -1, + 17, 59, 195, 60, 210, -1, 141, 202, 198, -1, + 141, 11, 202, 198, -1, 141, 202, 198, -1, 141, + 59, 195, 60, 210, -1, 202, -1, -1, 211, -1, + 59, 195, 60, -1, 202, -1, 3, -1, 50, 3, + -1, 141, -1, 212, -1, 59, 212, 49, 212, 60, + -1, -1, 214, 199, -1, 207, -1, 215, 75, 207, + -1, 208, -1, 216, 62, 208, -1, 209, -1, 217, + 62, 209, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -676,36 +661,36 @@ static const yytype_uint16 yyrline[] = 259, 260, 267, 267, 280, 284, 285, 289, 294, 300, 304, 308, 312, 318, 324, 330, 335, 339, 343, 349, 355, 359, 363, 369, 373, 379, 380, 384, 390, 399, - 405, 409, 414, 426, 442, 447, 454, 474, 492, 501, - 520, 519, 531, 530, 561, 564, 571, 570, 581, 587, - 596, 607, 613, 616, 624, 623, 634, 640, 652, 656, - 661, 651, 673, 676, 680, 687, 691, 686, 709, 708, - 724, 725, 729, 733, 737, 741, 745, 749, 753, 757, - 761, 765, 769, 773, 777, 781, 785, 789, 793, 797, - 802, 808, 809, 813, 824, 828, 832, 836, 841, 845, - 855, 859, 864, 872, 876, 877, 888, 892, 896, 900, - 904, 905, 911, 918, 924, 931, 934, 941, 947, 948, - 955, 956, 974, 975, 978, 981, 985, 996, 1005, 1011, - 1014, 1017, 1024, 1025, 1031, 1040, 1048, 1060, 1065, 1071, - 1072, 1073, 1074, 1075, 1076, 1082, 1083, 1084, 1085, 1091, - 1092, 1093, 1094, 1095, 1101, 1102, 1105, 1108, 1109, 1110, - 1111, 1112, 1115, 1116, 1129, 1133, 1138, 1143, 1148, 1152, - 1153, 1156, 1162, 1169, 1175, 1182, 1188, 1199, 1210, 1239, - 1278, 1301, 1318, 1327, 1330, 1338, 1342, 1346, 1353, 1359, - 1364, 1376, 1379, 1387, 1388, 1394, 1395, 1401, 1405, 1411, - 1412, 1418, 1422, 1428, 1451, 1456, 1462, 1468, 1475, 1484, - 1493, 1508, 1514, 1519, 1523, 1530, 1543, 1544, 1550, 1556, - 1559, 1563, 1569, 1572, 1581, 1584, 1585, 1589, 1590, 1596, - 1597, 1598, 1599, 1600, 1602, 1601, 1616, 1621, 1625, 1629, - 1633, 1637, 1642, 1661, 1667, 1675, 1679, 1685, 1689, 1695, - 1699, 1705, 1709, 1718, 1722, 1726, 1730, 1736, 1739, 1747, - 1748, 1750, 1751, 1754, 1757, 1760, 1763, 1766, 1769, 1772, - 1775, 1778, 1781, 1784, 1787, 1790, 1793, 1799, 1803, 1807, - 1811, 1815, 1819, 1837, 1844, 1855, 1856, 1857, 1860, 1861, - 1864, 1868, 1878, 1882, 1886, 1890, 1894, 1898, 1902, 1908, - 1914, 1922, 1930, 1936, 1943, 1959, 1977, 1981, 1987, 1990, - 1993, 1997, 2007, 2011, 2026, 2034, 2035, 2045, 2046, 2049, - 2053, 2059, 2063, 2069, 2073 + 405, 423, 428, 440, 456, 461, 468, 488, 506, 515, + 534, 533, 545, 544, 575, 578, 585, 584, 595, 601, + 610, 621, 627, 630, 638, 637, 648, 654, 666, 670, + 675, 665, 696, 695, 708, 711, 717, 720, 732, 736, + 731, 754, 753, 769, 770, 774, 778, 782, 786, 790, + 794, 798, 802, 806, 810, 814, 818, 822, 826, 830, + 834, 838, 842, 847, 853, 854, 858, 869, 873, 877, + 881, 886, 890, 900, 904, 909, 917, 921, 922, 933, + 937, 941, 945, 949, 950, 956, 963, 969, 976, 979, + 986, 992, 1009, 1016, 1017, 1024, 1025, 1044, 1045, 1048, + 1051, 1055, 1066, 1075, 1081, 1084, 1087, 1094, 1095, 1101, + 1116, 1124, 1136, 1141, 1147, 1148, 1149, 1150, 1151, 1152, + 1158, 1159, 1160, 1161, 1167, 1168, 1169, 1170, 1171, 1177, + 1178, 1181, 1184, 1185, 1186, 1187, 1188, 1191, 1192, 1205, + 1209, 1214, 1219, 1224, 1228, 1229, 1232, 1238, 1245, 1251, + 1258, 1264, 1275, 1289, 1318, 1358, 1383, 1401, 1410, 1413, + 1421, 1425, 1429, 1436, 1442, 1447, 1459, 1462, 1472, 1473, + 1479, 1480, 1486, 1490, 1496, 1497, 1503, 1507, 1513, 1536, + 1541, 1547, 1553, 1560, 1569, 1578, 1593, 1599, 1604, 1608, + 1615, 1628, 1629, 1635, 1641, 1644, 1648, 1654, 1657, 1666, + 1669, 1670, 1674, 1675, 1681, 1682, 1683, 1684, 1685, 1687, + 1686, 1701, 1706, 1710, 1714, 1718, 1722, 1727, 1746, 1752, + 1760, 1764, 1770, 1774, 1780, 1784, 1790, 1794, 1803, 1807, + 1811, 1815, 1821, 1824, 1832, 1833, 1835, 1836, 1839, 1842, + 1845, 1848, 1851, 1854, 1857, 1860, 1863, 1866, 1869, 1872, + 1875, 1878, 1884, 1888, 1892, 1896, 1900, 1904, 1924, 1931, + 1942, 1943, 1944, 1947, 1948, 1951, 1955, 1965, 1969, 1973, + 1977, 1981, 1985, 1989, 1995, 2001, 2009, 2017, 2023, 2030, + 2046, 2064, 2068, 2074, 2077, 2080, 2084, 2094, 2098, 2113, + 2121, 2122, 2134, 2135, 2138, 2142, 2148, 2152, 2158, 2162 }; #endif @@ -714,8 +699,8 @@ static const yytype_uint16 yyrline[] = First, the terminals, then, starting at YYNTOKENS, nonterminals. */ const char *yytname[] = { - "$end", "error", "$undefined", "LLITERAL", "LASOP", "LBREAK", "LCASE", - "LCHAN", "LCOLAS", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER", + "$end", "error", "$undefined", "LLITERAL", "LASOP", "LCOLAS", "LBREAK", + "LCASE", "LCHAN", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER", "LELSE", "LFALL", "LFOR", "LFUNC", "LGO", "LGOTO", "LIF", "LIMPORT", "LINTERFACE", "LMAP", "LNAME", "LPACKAGE", "LRANGE", "LRETURN", "LSELECT", "LSTRUCT", "LSWITCH", "LTYPE", "LVAR", "LANDAND", "LANDNOT", @@ -724,30 +709,31 @@ const char *yytname[] = "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'", "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'", "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file", - "package", "loadsys", "$@1", "imports", "import", "import_stmt", + "package", "loadsys", "@1", "imports", "import", "import_stmt", "import_stmt_list", "import_here", "import_package", "import_safety", - "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl", + "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl", "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case", - "compound_stmt", "$@3", "caseblock", "$@4", "caseblock_list", - "loop_body", "$@5", "range_stmt", "for_header", "for_body", "for_stmt", - "$@6", "if_header", "if_stmt", "$@7", "$@8", "$@9", "else", - "switch_stmt", "$@10", "$@11", "select_stmt", "$@12", "expr", "uexpr", - "pseudocall", "pexpr_no_paren", "start_complit", "keyval", "complitexpr", - "pexpr", "expr_or_type", "name_or_type", "lbrace", "new_name", - "dcl_name", "onew_name", "sym", "hidden_importsym", "name", "labelname", - "dotdotdot", "ntype", "non_expr_type", "non_recvchantype", "convtype", - "comptype", "fnret_type", "dotname", "othertype", "ptrtype", - "recvchantype", "structtype", "interfacetype", "xfndcl", "fndcl", - "hidden_fndcl", "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", - "xdcl_list", "vardcl_list", "constdcl_list", "typedcl_list", - "structdcl_list", "interfacedcl_list", "structdcl", "packname", "embed", - "interfacedcl", "indcl", "arg_type", "arg_type_list", - "oarg_type_list_ocomma", "stmt", "non_dcl_stmt", "$@13", "stmt_list", - "new_name_list", "dcl_name_list", "expr_list", "expr_or_type_list", - "keyval_list", "braced_keyval_list", "osemi", "ocomma", "oexpr", - "oexpr_list", "osimple_stmt", "ohidden_funarg_list", - "ohidden_structdcl_list", "ohidden_interfacedcl_list", "oliteral", - "hidden_import", "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type", + "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body", + "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6", + "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list", + "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr", + "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval", + "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type", + "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym", + "hidden_importsym", "name", "labelname", "dotdotdot", "ntype", + "non_expr_type", "non_recvchantype", "convtype", "comptype", + "fnret_type", "dotname", "othertype", "ptrtype", "recvchantype", + "structtype", "interfacetype", "xfndcl", "fndcl", "hidden_fndcl", + "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", "xdcl_list", + "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list", + "interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl", + "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt", + "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list", + "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list", + "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt", + "ohidden_funarg_list", "ohidden_structdcl_list", + "ohidden_interfacedcl_list", "oliteral", "hidden_import", + "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type", "hidden_type_non_recv_chan", "hidden_type_misc", "hidden_type_recv_chan", "hidden_type_func", "hidden_funarg", "hidden_structdcl", "hidden_interfacedcl", "ohidden_funres", "hidden_funres", @@ -784,33 +770,33 @@ static const yytype_uint8 yyr1[] = 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 101, 100, 103, 102, 104, 104, 106, 105, 107, 107, 108, 108, 108, 109, 111, 110, 112, 112, 114, 115, - 116, 113, 117, 117, 117, 119, 120, 118, 122, 121, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 127, 128, 129, 129, - 130, 130, 131, 131, 132, 133, 133, 134, 135, 136, - 136, 137, 137, 137, 138, 139, 140, 141, 141, 142, - 142, 142, 142, 142, 142, 143, 143, 143, 143, 144, - 144, 144, 144, 144, 145, 145, 146, 147, 147, 147, - 147, 147, 148, 148, 149, 149, 149, 149, 149, 149, - 149, 150, 151, 152, 152, 153, 153, 154, 155, 155, - 156, 156, 157, 158, 158, 159, 159, 159, 160, 161, - 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, - 166, 167, 167, 168, 168, 168, 168, 168, 168, 169, - 169, 170, 171, 171, 171, 172, 173, 173, 173, 173, - 174, 174, 175, 175, 176, 176, 176, 176, 176, 177, - 177, 177, 177, 177, 178, 177, 177, 177, 177, 177, - 177, 177, 177, 179, 179, 180, 180, 181, 181, 182, - 182, 183, 183, 184, 184, 184, 184, 185, 185, 186, - 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, - 191, 192, 192, 193, 193, 194, 194, 195, 195, 195, - 195, 195, 195, 196, 197, 198, 198, 198, 199, 199, - 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, - 200, 201, 202, 203, 203, 204, 205, 205, 206, 206, - 207, 207, 208, 208, 208, 209, 209, 210, 210, 211, - 211, 212, 212, 213, 213 + 116, 113, 118, 117, 119, 119, 120, 120, 122, 123, + 121, 125, 124, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 128, 128, 128, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 130, + 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, + 137, 137, 138, 139, 140, 140, 141, 141, 141, 142, + 143, 144, 145, 145, 146, 146, 146, 146, 146, 146, + 147, 147, 147, 147, 148, 148, 148, 148, 148, 149, + 149, 150, 151, 151, 151, 151, 151, 152, 152, 153, + 153, 153, 153, 153, 153, 153, 154, 155, 156, 156, + 157, 157, 158, 159, 159, 160, 160, 161, 162, 162, + 163, 163, 163, 164, 165, 165, 166, 166, 167, 167, + 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, + 172, 172, 172, 172, 173, 173, 174, 175, 175, 175, + 176, 177, 177, 177, 177, 178, 178, 179, 179, 180, + 180, 180, 180, 180, 181, 181, 181, 181, 181, 182, + 181, 181, 181, 181, 181, 181, 181, 181, 183, 183, + 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, + 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, + 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, + 198, 198, 199, 199, 199, 199, 199, 199, 200, 201, + 202, 202, 202, 203, 203, 204, 204, 204, 204, 204, + 204, 204, 204, 204, 204, 204, 205, 206, 207, 207, + 208, 209, 209, 210, 210, 211, 211, 212, 212, 212, + 213, 213, 214, 214, 215, 215, 216, 216, 217, 217 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -824,663 +810,674 @@ static const yytype_uint8 yyr2[] = 1, 3, 3, 3, 2, 2, 3, 5, 5, 2, 0, 4, 0, 3, 0, 2, 0, 4, 4, 4, 5, 1, 1, 2, 0, 3, 1, 3, 0, 0, - 0, 7, 0, 2, 2, 0, 0, 7, 0, 5, - 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 8, 0, 5, 0, 2, 0, 2, 0, 0, + 7, 0, 5, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 1, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 5, 6, 1, 1, 3, 5, 5, 4, 6, - 1, 4, 5, 5, 7, 1, 0, 3, 1, 4, - 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, - 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, - 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 3, 4, 4, 2, 3, 5, 1, - 1, 2, 3, 5, 3, 5, 3, 3, 5, 8, - 5, 8, 5, 0, 3, 0, 1, 3, 1, 4, - 2, 0, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 3, 2, 4, 3, 5, 5, 1, - 3, 1, 2, 1, 3, 4, 1, 2, 2, 1, - 1, 3, 0, 2, 0, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 4, 1, 2, 2, 2, - 2, 2, 2, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 1, 3, 3, 0, 2, 0, - 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 1, 0, 1, 4, 4, 5, - 6, 4, 4, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 4, 5, 4, 4, 2, 2, 4, - 3, 3, 5, 3, 4, 3, 5, 1, 0, 1, - 3, 1, 1, 2, 1, 1, 5, 0, 2, 1, - 3, 1, 3, 1, 3 + 3, 3, 3, 3, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 5, 6, 1, 1, 3, 5, + 5, 4, 6, 1, 5, 5, 5, 7, 1, 0, + 3, 1, 4, 1, 4, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 4, + 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, + 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 4, + 4, 2, 3, 5, 1, 1, 2, 3, 5, 3, + 5, 3, 3, 5, 8, 5, 8, 5, 0, 3, + 0, 1, 3, 1, 4, 2, 0, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 3, 2, + 4, 3, 5, 5, 1, 3, 1, 2, 1, 3, + 4, 1, 2, 2, 1, 1, 3, 0, 2, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 4, 1, 2, 2, 2, 2, 2, 2, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 1, + 3, 3, 0, 2, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 4, 4, 5, 6, 4, 4, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 4, 5, + 4, 4, 2, 2, 4, 3, 3, 5, 3, 4, + 3, 5, 1, 0, 1, 3, 1, 1, 2, 1, + 1, 5, 0, 2, 1, 3, 1, 3, 1, 3 }; -/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE doesn't specify something else to do. Zero +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { - 5, 0, 3, 0, 1, 0, 7, 0, 22, 151, - 153, 0, 0, 152, 211, 20, 6, 337, 0, 4, + 5, 0, 3, 0, 1, 0, 7, 0, 22, 156, + 158, 0, 0, 157, 216, 20, 6, 342, 0, 4, 0, 0, 0, 21, 0, 0, 0, 16, 0, 0, - 9, 22, 0, 8, 28, 123, 149, 0, 39, 149, - 0, 256, 74, 0, 0, 0, 78, 0, 0, 285, - 88, 0, 85, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 283, 0, 25, 0, 249, 250, - 253, 251, 252, 50, 90, 130, 140, 111, 156, 155, - 124, 0, 0, 0, 176, 189, 190, 26, 208, 0, - 135, 27, 0, 19, 0, 0, 0, 0, 0, 0, - 338, 154, 11, 14, 279, 18, 22, 13, 17, 150, - 257, 147, 0, 0, 0, 0, 155, 182, 186, 172, - 170, 171, 169, 258, 130, 0, 287, 242, 0, 203, - 130, 261, 287, 145, 146, 0, 0, 269, 286, 262, - 0, 0, 287, 0, 0, 36, 48, 0, 29, 267, - 148, 0, 119, 114, 115, 118, 112, 113, 0, 0, - 142, 0, 143, 167, 165, 166, 116, 117, 0, 284, - 0, 212, 0, 32, 0, 0, 0, 0, 0, 55, + 9, 22, 0, 8, 28, 126, 154, 0, 39, 154, + 0, 261, 74, 0, 0, 0, 78, 0, 0, 290, + 91, 0, 88, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 288, 0, 25, 0, 254, 255, + 258, 256, 257, 50, 93, 133, 145, 114, 161, 160, + 127, 0, 0, 0, 181, 194, 195, 26, 213, 0, + 138, 27, 0, 19, 0, 0, 0, 0, 0, 0, + 343, 159, 11, 14, 284, 18, 22, 13, 17, 155, + 262, 152, 0, 0, 0, 0, 160, 187, 191, 177, + 175, 176, 174, 263, 133, 0, 292, 247, 0, 208, + 133, 266, 292, 150, 151, 0, 0, 274, 291, 267, + 0, 0, 292, 0, 0, 36, 48, 0, 29, 272, + 153, 0, 122, 117, 118, 121, 115, 116, 0, 0, + 147, 0, 148, 172, 170, 171, 119, 120, 0, 289, + 0, 217, 0, 32, 0, 0, 0, 0, 0, 55, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 136, 0, - 0, 283, 254, 0, 136, 210, 0, 0, 0, 0, - 303, 0, 0, 203, 0, 0, 304, 0, 0, 23, - 280, 0, 12, 242, 0, 0, 187, 163, 161, 162, - 159, 160, 191, 0, 0, 288, 72, 0, 75, 0, - 71, 157, 236, 155, 239, 144, 240, 281, 0, 242, - 0, 197, 79, 76, 151, 0, 196, 0, 279, 233, - 221, 0, 64, 0, 0, 194, 265, 279, 219, 231, - 295, 0, 86, 38, 217, 279, 49, 31, 213, 279, - 0, 0, 40, 0, 168, 141, 0, 0, 35, 279, - 0, 0, 51, 92, 107, 110, 93, 97, 98, 96, - 108, 95, 94, 91, 109, 99, 100, 101, 102, 103, - 104, 105, 106, 277, 120, 271, 281, 0, 125, 284, - 0, 0, 0, 277, 248, 60, 246, 245, 263, 247, - 0, 53, 52, 270, 0, 0, 0, 0, 311, 0, - 0, 0, 0, 0, 310, 0, 305, 306, 307, 0, - 339, 0, 0, 289, 0, 0, 0, 15, 10, 0, - 0, 0, 173, 183, 66, 73, 0, 0, 287, 158, - 237, 238, 282, 243, 205, 0, 0, 0, 287, 0, - 229, 0, 242, 232, 280, 0, 0, 0, 0, 295, - 0, 0, 280, 0, 296, 224, 0, 295, 0, 280, - 0, 280, 0, 42, 268, 0, 0, 0, 192, 163, - 161, 162, 160, 136, 185, 184, 280, 0, 44, 0, - 136, 138, 273, 274, 281, 0, 281, 282, 0, 0, - 0, 128, 283, 255, 131, 0, 0, 0, 209, 0, - 0, 318, 308, 309, 289, 293, 0, 291, 0, 317, - 332, 0, 0, 334, 335, 0, 0, 0, 0, 0, - 295, 0, 0, 302, 0, 290, 297, 301, 298, 205, - 164, 0, 0, 0, 0, 241, 242, 155, 206, 181, - 179, 180, 177, 178, 202, 205, 204, 80, 77, 230, - 234, 0, 222, 195, 188, 0, 0, 89, 62, 65, - 0, 226, 0, 295, 220, 193, 266, 223, 64, 218, - 37, 214, 30, 41, 0, 277, 45, 215, 279, 47, - 33, 43, 277, 0, 282, 278, 133, 282, 0, 272, - 121, 127, 126, 0, 132, 0, 264, 320, 0, 0, - 311, 0, 310, 0, 327, 343, 294, 0, 0, 0, - 341, 292, 321, 333, 0, 299, 0, 312, 0, 295, - 323, 0, 340, 328, 0, 69, 68, 287, 0, 242, - 198, 82, 205, 0, 59, 0, 295, 295, 225, 0, - 164, 0, 280, 0, 46, 0, 138, 137, 275, 276, - 122, 129, 61, 319, 328, 289, 316, 0, 0, 295, - 315, 0, 0, 313, 300, 324, 289, 289, 331, 200, - 329, 67, 70, 207, 0, 0, 81, 235, 0, 0, - 56, 0, 63, 228, 227, 87, 134, 216, 34, 139, - 322, 0, 344, 314, 325, 342, 0, 0, 0, 205, - 84, 83, 0, 0, 328, 336, 328, 330, 199, 58, - 57, 326, 201 + 0, 0, 0, 0, 0, 0, 0, 0, 139, 0, + 0, 288, 259, 0, 139, 215, 0, 0, 0, 0, + 308, 0, 0, 208, 0, 0, 309, 0, 0, 23, + 285, 0, 12, 247, 0, 0, 192, 168, 166, 167, + 164, 165, 196, 0, 0, 293, 72, 0, 75, 0, + 71, 162, 241, 160, 244, 149, 245, 286, 0, 247, + 0, 202, 79, 76, 156, 0, 201, 0, 284, 238, + 226, 0, 64, 0, 0, 199, 270, 284, 224, 236, + 300, 0, 89, 38, 222, 284, 49, 31, 218, 284, + 0, 0, 40, 0, 173, 146, 0, 0, 35, 284, + 0, 0, 51, 95, 110, 113, 96, 100, 101, 99, + 111, 98, 97, 94, 112, 102, 103, 104, 105, 106, + 107, 108, 109, 282, 123, 276, 286, 0, 128, 289, + 0, 0, 286, 282, 253, 60, 251, 250, 268, 252, + 0, 53, 52, 275, 0, 0, 0, 0, 316, 0, + 0, 0, 0, 0, 315, 0, 310, 311, 312, 0, + 344, 0, 0, 294, 0, 0, 0, 15, 10, 0, + 0, 0, 178, 188, 66, 73, 0, 0, 292, 163, + 242, 243, 287, 248, 210, 0, 0, 0, 292, 0, + 234, 0, 247, 237, 285, 0, 0, 0, 0, 300, + 0, 0, 285, 0, 301, 229, 0, 300, 0, 285, + 0, 285, 0, 42, 273, 0, 0, 0, 197, 168, + 166, 167, 165, 139, 190, 189, 285, 0, 44, 0, + 139, 141, 278, 279, 286, 0, 286, 287, 0, 0, + 0, 131, 288, 260, 287, 0, 0, 0, 0, 214, + 0, 0, 323, 313, 314, 294, 298, 0, 296, 0, + 322, 337, 0, 0, 339, 340, 0, 0, 0, 0, + 0, 300, 0, 0, 307, 0, 295, 302, 306, 303, + 210, 169, 0, 0, 0, 0, 246, 247, 160, 211, + 186, 184, 185, 182, 183, 207, 210, 209, 80, 77, + 235, 239, 0, 227, 200, 193, 0, 0, 92, 62, + 65, 0, 231, 0, 300, 225, 198, 271, 228, 64, + 223, 37, 219, 30, 41, 0, 282, 45, 220, 284, + 47, 33, 43, 282, 0, 287, 283, 136, 0, 277, + 124, 130, 129, 0, 134, 135, 0, 269, 325, 0, + 0, 316, 0, 315, 0, 332, 348, 299, 0, 0, + 0, 346, 297, 326, 338, 0, 304, 0, 317, 0, + 300, 328, 0, 345, 333, 0, 69, 68, 292, 0, + 247, 203, 84, 210, 0, 59, 0, 300, 300, 230, + 0, 169, 0, 285, 0, 46, 0, 139, 143, 140, + 280, 281, 125, 132, 61, 324, 333, 294, 321, 0, + 0, 300, 320, 0, 0, 318, 305, 329, 294, 294, + 336, 205, 334, 67, 70, 212, 0, 86, 240, 0, + 0, 56, 0, 63, 233, 232, 90, 137, 221, 34, + 142, 282, 327, 0, 349, 319, 330, 347, 0, 0, + 0, 210, 0, 85, 81, 0, 0, 0, 333, 341, + 333, 335, 204, 82, 87, 58, 57, 144, 331, 206, + 292, 0, 83 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 6, 2, 3, 14, 21, 30, 104, 31, - 8, 24, 16, 17, 65, 326, 67, 148, 516, 517, - 144, 145, 68, 498, 327, 436, 499, 575, 387, 365, - 471, 236, 237, 238, 69, 126, 252, 70, 132, 377, - 571, 616, 71, 142, 398, 72, 140, 73, 74, 75, - 76, 313, 422, 423, 77, 315, 242, 135, 78, 149, - 110, 116, 13, 80, 81, 244, 245, 162, 118, 82, - 83, 478, 227, 84, 229, 230, 85, 86, 87, 129, - 213, 88, 251, 484, 89, 90, 22, 279, 518, 275, - 267, 258, 268, 269, 270, 260, 383, 246, 247, 248, - 328, 329, 321, 330, 271, 151, 92, 316, 424, 425, - 221, 373, 170, 139, 253, 464, 549, 543, 395, 100, - 211, 217, 608, 441, 346, 347, 348, 350, 550, 545, - 609, 610, 454, 455, 25, 465, 551, 546 + 8, 24, 16, 17, 65, 326, 67, 148, 517, 518, + 144, 145, 68, 499, 327, 437, 500, 576, 387, 365, + 472, 236, 237, 238, 69, 126, 252, 70, 132, 377, + 572, 643, 660, 617, 644, 71, 142, 398, 72, 140, + 73, 74, 75, 76, 313, 422, 423, 589, 77, 315, + 242, 135, 78, 149, 110, 116, 13, 80, 81, 244, + 245, 162, 118, 82, 83, 479, 227, 84, 229, 230, + 85, 86, 87, 129, 213, 88, 251, 485, 89, 90, + 22, 279, 519, 275, 267, 258, 268, 269, 270, 260, + 383, 246, 247, 248, 328, 329, 321, 330, 271, 151, + 92, 316, 424, 425, 221, 373, 170, 139, 253, 465, + 550, 544, 395, 100, 211, 217, 610, 442, 346, 347, + 348, 350, 551, 546, 611, 612, 455, 456, 25, 466, + 552, 547 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -551 +#define YYPACT_NINF -485 static const yytype_int16 yypact[] = { - -551, 46, 48, 121, -551, 58, -551, 135, -551, -551, - -551, 65, 72, -551, 147, 156, -551, -551, 112, -551, - 50, 134, 904, -551, 162, 463, 209, -551, 54, 237, - -551, 121, 245, -551, -551, -551, 58, 1666, -551, 58, - 288, -551, -551, 34, 288, 58, -551, 36, 184, 1467, - -551, 36, -551, 316, 462, 1467, 1467, 1467, 1467, 1467, - 1467, 1522, 1467, 1467, 965, 198, -551, 506, -551, -551, - -551, -551, -551, 796, -551, -551, 195, 1, -551, 199, - -551, 207, 216, 36, 221, -551, -551, -551, 226, 53, - -551, -551, 108, -551, 214, 110, 269, 214, 214, 233, - -551, -551, -551, -551, 238, -551, -551, -551, -551, -551, - -551, -551, 242, 1691, 1691, 1691, -551, 240, -551, -551, - -551, -551, -551, -551, 154, 1, 1467, 1658, 247, 246, - 290, -551, 1467, -551, -551, 419, 1691, 2004, 229, -551, - 272, 333, 1467, 258, 1691, -551, -551, 485, -551, -551, - -551, 656, -551, -551, -551, -551, -551, -551, 1577, 1522, - 2004, 255, -551, 10, -551, 51, -551, -551, 251, 2004, - 256, -551, 508, -551, 1632, 1467, 1467, 1467, 1467, -551, - 1467, 1467, 1467, -551, 1467, 1467, 1467, 1467, 1467, 1467, - 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, -551, 1192, - 552, 1467, -551, 1467, -551, -551, 1134, 1467, 1467, 1467, - -551, 1203, 58, 246, 260, 332, -551, 1838, 1838, -551, - 76, 282, -551, 1658, 344, 1691, -551, -551, -551, -551, - -551, -551, -551, 293, 58, -551, -551, 328, -551, 178, - 303, 1691, -551, 1658, -551, -551, -551, 295, 308, 1658, - 1134, -551, -551, 309, 271, 364, -551, 334, 337, -551, - -551, 330, -551, 11, 66, -551, -551, 342, -551, -551, - 397, 664, -551, -551, -551, 351, -551, -551, -551, 352, - 1467, 58, 343, 1719, -551, 349, 1691, 1691, -551, 356, - 1467, 357, 2004, 2075, -551, 2028, 692, 692, 692, 692, - -551, 692, 692, 2052, -551, 593, 593, 593, 593, -551, - -551, -551, -551, 1247, -551, -551, 22, 1302, -551, 1877, - 355, 1060, 1979, 1247, -551, -551, -551, -551, -551, -551, - 86, 229, 229, 2004, 1777, 367, 361, 359, -551, 366, - 427, 1838, 52, 29, -551, 370, -551, -551, -551, 784, - -551, 118, 379, 58, 396, 400, 401, -551, -551, 404, - 1691, 409, -551, -551, -551, -551, 1357, 1412, 1467, -551, - -551, -551, 1658, -551, 1744, 414, 109, 328, 1467, 58, - 416, 423, 1658, -551, 561, 417, 1691, 44, 364, 397, - 364, 428, 451, 421, -551, -551, 58, 397, 456, 58, - 436, 58, 438, 229, -551, 1467, 1752, 1691, -551, 24, - 171, 287, 338, -551, -551, -551, 58, 439, 229, 1467, - -551, 1907, -551, -551, 425, 435, 430, 1522, 446, 448, - 453, -551, 1467, -551, -551, 443, 1134, 1060, -551, 1838, - 479, -551, -551, -551, 58, 1805, 1838, 58, 1838, -551, - -551, 514, 161, -551, -551, 460, 454, 1838, 52, 1838, - 397, 58, 58, -551, 458, 459, -551, -551, -551, 1744, - -551, 1134, 1467, 1467, 467, -551, 1658, 472, -551, -551, - -551, -551, -551, -551, -551, 1744, -551, -551, -551, -551, - -551, 477, -551, -551, -551, 1522, 474, -551, -551, -551, - 482, -551, 500, 397, -551, -551, -551, -551, -551, -551, - -551, -551, -551, 229, 501, 1247, -551, -551, 505, 1632, - -551, 229, 1247, 1247, 1247, -551, -551, -551, 503, -551, - -551, -551, -551, 511, -551, 137, -551, -551, 518, 528, - 532, 534, 535, 530, -551, -551, 537, 533, 1838, 536, - -551, 538, -551, -551, 560, -551, 1838, -551, 551, 397, - -551, 557, -551, 1830, 151, 2004, 2004, 1467, 558, 1658, - -551, 605, 1744, 125, -551, 1060, 397, 397, -551, 75, - 360, 554, 58, 563, 357, 556, 2004, -551, -551, -551, - -551, -551, -551, -551, 1830, 58, -551, 1805, 1838, 397, - -551, 58, 161, -551, -551, -551, 58, 58, -551, -551, - -551, -551, -551, -551, 570, 78, -551, -551, 1467, 1467, - -551, 1522, 569, -551, -551, -551, -551, -551, -551, -551, - -551, 576, -551, -551, -551, -551, 578, 582, 584, 1744, - -551, -551, 1931, 1955, 1830, -551, 1830, -551, -551, -551, - -551, -551, -551 + -485, 67, 35, 55, -485, 44, -485, 64, -485, -485, + -485, 96, 38, -485, 77, 85, -485, -485, 66, -485, + 34, 84, 1059, -485, 86, 294, 147, -485, 165, 210, + -485, 55, 221, -485, -485, -485, 44, 1762, -485, 44, + 290, -485, -485, 442, 290, 44, -485, 80, 69, 1608, + -485, 80, -485, 450, 452, 1608, 1608, 1608, 1608, 1608, + 1608, 1651, 1608, 1608, 920, 157, -485, 460, -485, -485, + -485, -485, -485, 718, -485, -485, 167, 344, -485, 176, + -485, 180, 193, 80, 206, -485, -485, -485, 218, 91, + -485, -485, 76, -485, 205, 10, 260, 205, 205, 223, + -485, -485, -485, -485, 230, -485, -485, -485, -485, -485, + -485, -485, 237, 1770, 1770, 1770, -485, 236, -485, -485, + -485, -485, -485, -485, 220, 344, 1608, 990, 241, 235, + 262, -485, 1608, -485, -485, 405, 1770, 2090, 254, -485, + 297, 444, 1608, 61, 1770, -485, -485, 271, -485, -485, + -485, 671, -485, -485, -485, -485, -485, -485, 1694, 1651, + 2090, 291, -485, 181, -485, 60, -485, -485, 287, 2090, + 301, -485, 496, -485, 912, 1608, 1608, 1608, 1608, -485, + 1608, 1608, 1608, -485, 1608, 1608, 1608, 1608, 1608, 1608, + 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, -485, 1290, + 468, 1608, -485, 1608, -485, -485, 1221, 1608, 1608, 1608, + -485, 573, 44, 235, 275, 347, -485, 1301, 1301, -485, + 113, 302, -485, 990, 358, 1770, -485, -485, -485, -485, + -485, -485, -485, 316, 44, -485, -485, 340, -485, 78, + 318, 1770, -485, 990, -485, -485, -485, 307, 325, 990, + 1221, -485, -485, 324, 117, 365, -485, 343, 337, -485, + -485, 333, -485, 32, 23, -485, -485, 350, -485, -485, + 406, 1737, -485, -485, -485, 351, -485, -485, -485, 352, + 1608, 44, 354, 1796, -485, 353, 1770, 1770, -485, 359, + 1608, 357, 2090, 1928, -485, 2114, 1212, 1212, 1212, 1212, + -485, 1212, 1212, 2138, -485, 566, 566, 566, 566, -485, + -485, -485, -485, 1345, -485, -485, 31, 1400, -485, 1988, + 360, 1147, 1955, 1345, -485, -485, -485, -485, -485, -485, + 95, 254, 254, 2090, 1857, 368, 361, 371, -485, 363, + 427, 1301, 247, 51, -485, 374, -485, -485, -485, 1890, + -485, 36, 382, 44, 384, 385, 387, -485, -485, 391, + 1770, 395, -485, -485, -485, -485, 1455, 1510, 1608, -485, + -485, -485, 990, -485, 1823, 399, 135, 340, 1608, 44, + 397, 403, 990, -485, 542, 407, 1770, 278, 365, 406, + 365, 411, 364, 413, -485, -485, 44, 406, 430, 44, + 423, 44, 425, 254, -485, 1608, 1849, 1770, -485, 216, + 219, 274, 288, -485, -485, -485, 44, 426, 254, 1608, + -485, 2018, -485, -485, 414, 422, 416, 1651, 433, 434, + 436, -485, 1608, -485, -485, 439, 437, 1221, 1147, -485, + 1301, 466, -485, -485, -485, 44, 1882, 1301, 44, 1301, + -485, -485, 504, 207, -485, -485, 446, 438, 1301, 247, + 1301, 406, 44, 44, -485, 453, 455, -485, -485, -485, + 1823, -485, 1221, 1608, 1608, 467, -485, 990, 472, -485, + -485, -485, -485, -485, -485, -485, 1823, -485, -485, -485, + -485, -485, 475, -485, -485, -485, 1651, 470, -485, -485, + -485, 490, -485, 493, 406, -485, -485, -485, -485, -485, + -485, -485, -485, -485, 254, 495, 1345, -485, -485, 498, + 912, -485, 254, 1345, 1553, 1345, -485, -485, 497, -485, + -485, -485, -485, 486, -485, -485, 143, -485, -485, 501, + 502, 473, 508, 513, 505, -485, -485, 515, 503, 1301, + 511, -485, 518, -485, -485, 533, -485, 1301, -485, 522, + 406, -485, 526, -485, 1916, 144, 2090, 2090, 1608, 527, + 990, -485, -485, 1823, 39, -485, 1147, 406, 406, -485, + 315, 293, 521, 44, 548, 357, 525, -485, 2090, -485, + -485, -485, -485, -485, -485, -485, 1916, 44, -485, 1882, + 1301, 406, -485, 44, 207, -485, -485, -485, 44, 44, + -485, -485, -485, -485, -485, -485, 551, 572, -485, 1608, + 1608, -485, 1651, 550, -485, -485, -485, -485, -485, -485, + -485, 1345, -485, 558, -485, -485, -485, -485, 563, 564, + 565, 1823, 46, -485, -485, 2042, 2066, 559, 1916, -485, + 1916, -485, -485, -485, -485, -485, -485, -485, -485, -485, + 1608, 340, -485 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -551, -551, -551, -551, -551, -551, -551, -6, -551, -551, - 608, -551, -11, -551, -551, 623, -551, -134, -25, 68, - -551, -135, -121, -551, 39, -551, -551, -551, 145, 278, - -551, -551, -551, -551, -551, -551, 515, 41, -551, -551, - -551, -551, -551, -551, -551, -551, -551, 579, 493, 45, - -551, -192, 138, -246, 192, -47, 415, 200, -20, 380, - 626, -5, 449, 346, -551, 426, 95, 509, -551, -551, - -551, -551, -33, 38, -31, -18, -551, -551, -551, -551, - -551, 43, 457, -467, -551, -551, -551, -551, -551, -551, - -551, -551, 280, -126, -227, 292, -551, 302, -551, -220, - -297, 662, -551, -248, -551, -66, 18, 194, -551, -295, - -228, -289, -191, -551, -119, -403, -551, -551, -305, -551, - -32, -551, 127, -551, 362, 250, 363, 232, 90, 98, - -550, -551, -426, 241, -551, 486, -551, -551 + -485, -485, -485, -485, -485, -485, -485, -6, -485, -485, + 597, -485, -3, -485, -485, 608, -485, -131, -28, 50, + -485, -135, -106, -485, -7, -485, -485, -485, 125, -370, + -485, -485, -485, -485, -485, -485, -138, -485, -485, -485, + -485, -485, -485, -485, -485, -485, -485, -485, -485, -485, + 665, 15, 116, -485, -190, 111, 112, -485, 164, -59, + 398, 137, 14, 367, 603, -5, 454, 432, -485, 402, + -50, 491, -485, -485, -485, -485, -36, 18, -34, -9, + -485, -485, -485, -485, -485, 257, 441, -445, -485, -485, + -485, -485, -485, -485, -485, -485, 259, -116, -218, 265, + -485, 284, -485, -217, -286, 636, -485, -237, -485, -62, + -24, 166, -485, -314, -246, -265, -177, -485, -115, -415, + -485, -485, -379, -485, -8, -485, 435, -485, 326, 225, + 327, 204, 65, 70, -484, -485, -426, 211, -485, 462, + -485, -485 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which - number is the opposite. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -270 + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -275 static const yytype_int16 yytable[] = { - 12, 174, 376, 359, 119, 235, 121, 240, 274, 259, - 320, 235, 323, 278, 161, 32, 109, 79, 570, 109, - 107, 235, 103, 32, 433, 131, 554, 428, 435, 375, - 385, 111, 456, 426, 111, 380, 389, 391, 128, 393, - 111, 539, 173, 164, 630, -176, 4, 400, 146, 150, - 495, 402, -208, 27, 205, 450, 496, 27, 9, -172, - 199, 417, 150, 214, 200, 216, 218, 138, 18, -175, - 388, 133, 201, 5, 9, 120, 9, -176, 9, 27, - 122, 495, 9, -172, 501, 124, -208, 496, 133, 130, - 380, -172, 507, 127, 651, 222, 652, 427, 46, 163, - 9, 457, 451, 134, 165, 617, 174, 10, 11, 28, - -174, 452, 497, 29, 102, 257, 207, 29, -208, 390, - 134, 266, 243, 10, 11, 10, 11, 10, 11, 381, - 111, 10, 11, 618, 19, 525, 111, 528, 146, 29, - 536, 164, 150, 625, 239, 325, 7, 289, 437, 10, - 11, 228, 228, 228, 438, 560, 231, 231, 231, 15, - -260, 500, 491, 502, 450, 228, -260, 150, 20, 212, - 231, 437, 648, 208, 228, 26, 636, 486, 461, 231, - 23, 164, 228, 209, 11, 9, 366, 231, 535, 228, - 619, 620, 631, 462, 231, 318, 33, 163, 578, 437, - 621, 79, 165, 637, 638, 592, -170, 349, 226, 232, - 233, 451, 228, 437, 357, 32, -260, 231, 243, 611, - 581, 515, -260, 564, 93, 331, 332, 585, 522, 363, - -170, 261, 125, 101, 10, 11, 125, 163, -170, 276, - 105, 533, 165, 367, 243, 79, 282, 235, 108, 474, - 409, 141, 411, 209, 605, 136, 568, 235, 259, 488, - 171, 228, 198, 228, 509, -147, 231, 511, 231, 291, - 430, 623, 624, 202, -229, 203, 150, 587, 589, 228, - -175, 228, 9, 204, 231, -174, 231, 228, 11, 206, - 583, 35, 231, 215, 634, 37, -259, 219, 403, 164, - 220, 223, -259, 234, 209, 112, 249, 262, 418, 228, - 47, 48, 9, 250, 231, 285, 79, 51, 273, 353, - 361, 410, -171, 286, 228, 228, 412, 622, 287, 231, - 231, 10, 11, -229, 379, 354, 369, 453, 345, -229, - 9, 479, 358, 481, 355, 356, -171, 61, 349, 614, - 519, 360, -259, 362, -171, 163, 482, 254, -259, 64, - 165, 10, 11, 364, 257, 368, 397, 243, 374, 477, - 372, 378, 266, -169, 489, 143, 506, 243, 408, 111, - 529, 414, 415, 117, 331, 332, 263, 111, 380, 10, - 11, 111, 264, 382, 146, -173, 150, -169, 228, 384, - 394, 265, 386, 231, 392, -169, 10, 11, 405, 164, - 228, 150, 480, 399, 401, 231, 413, 483, 416, -173, - 228, 432, 419, 513, 228, 231, 444, -173, 445, 231, - 446, 79, 79, 447, 448, 458, 479, 521, 481, 349, - 541, 463, 548, 254, 228, 228, 235, 453, 612, 231, - 231, 482, 479, 453, 481, 408, 561, 349, 466, 117, - 117, 117, 467, 468, 469, 163, 79, 482, 449, 470, - 165, 243, 94, 117, 485, 254, 460, 164, 255, 379, - 95, 494, 117, 490, 96, 493, 9, 256, 503, 505, - 117, 508, 10, 11, 97, 98, 510, 117, 512, 520, - 524, 226, 514, 526, 263, 527, 530, 480, 531, 9, - 264, 534, 483, 532, 228, 340, 519, 553, 563, 231, - 117, 147, 555, 480, 10, 11, 556, 99, 483, 567, - 9, 569, 9, 163, 462, 10, 11, 572, 165, 479, - 574, 481, 576, 210, 210, 277, 210, 210, 152, 153, - 154, 155, 156, 157, 482, 166, 167, 228, 10, 11, - 577, 580, 231, 590, 243, 172, 537, 582, 288, 117, - 79, 117, 544, 547, 529, 552, 9, 150, 593, 10, - 11, 10, 11, 591, 557, 254, 559, 117, 594, 117, - 349, -151, 541, 595, -152, 117, 548, 453, 596, 597, - 601, 349, 349, 164, 600, 598, 479, 228, 481, 602, - 480, 317, 231, 604, 584, 483, 606, 117, 613, 615, - 255, 482, 626, 628, 629, 10, 11, 177, 137, 117, - 639, 437, 117, 117, 10, 11, 644, 185, 645, 106, - 160, 189, 646, 169, 647, 66, 194, 195, 196, 197, - 627, 152, 156, 579, 640, 487, 641, 272, 370, 163, - 344, 404, 588, 37, 165, 123, 344, 344, 284, 371, - 352, 37, 504, 112, 475, 599, 492, 480, 47, 48, - 9, 112, 483, 603, 91, 51, 47, 48, 9, 573, - 538, 635, 224, 51, 562, 632, 442, 443, 351, 558, - 224, 0, 0, 0, 0, 0, 117, 0, 0, 114, - 0, 0, 0, 0, 0, 225, 0, 114, 117, 0, - 117, 280, 0, 225, 544, 633, 177, 64, 117, 10, - 11, 281, 117, 0, 0, 64, 185, 10, 11, 396, + 12, 119, 161, 121, 272, 174, 359, 488, 274, 436, + 502, 240, 385, 376, 323, 32, 278, 79, 508, 259, + 235, 393, 103, 32, 320, 138, 235, 555, 107, 400, + 540, 111, 375, 402, 111, 433, 235, 27, 128, 173, + 111, 571, 426, 417, 619, 389, 391, 380, 146, 150, + 109, 428, 164, 109, 457, 120, 380, 435, 9, 131, + 5, -213, 150, 226, 232, 233, 653, 4, 9, 212, + 152, 153, 154, 155, 156, 157, 390, 166, 167, 163, + 7, 207, 561, 366, 11, 9, 261, 214, 15, 216, + 218, 388, 205, 28, 276, -213, 462, 29, 20, 18, + 19, 282, 239, 222, 620, 621, 427, 10, 11, 23, + 174, 463, 632, 325, 622, 133, 27, 10, 11, -179, + -234, 273, 243, 458, 291, 579, 133, -213, 618, 26, + 111, 228, 228, 228, 10, 11, 111, 9, 146, 381, + 136, 208, 150, 367, 289, 228, 33, 134, 93, 257, + 164, 209, 537, 209, 228, 266, 124, 438, 134, 526, + 130, 528, 228, 439, 658, 492, 659, 150, 27, 228, + 501, 101, 503, 152, 156, 361, 29, 163, 638, -234, + 379, 607, 633, 331, 332, -234, 10, 11, 141, 9, + 164, 369, 228, 639, 640, 318, 652, 438, 624, 625, + 536, 79, 582, 487, 125, 438, 438, 349, 125, 586, + 451, 594, 613, 105, 357, 32, -181, 163, 243, 171, + 204, 397, 636, 516, 108, 102, 206, -265, 29, 363, + 523, 9, -265, 408, 198, 565, 414, 415, 10, 11, + -180, 228, -152, 228, 243, 79, 202, 409, -181, 411, + 451, -177, 203, 475, -175, 533, 403, 452, 430, 228, + 569, 228, 235, 489, 510, -180, 418, 228, 259, -264, + 512, 9, 235, 584, -264, -177, 150, -179, -175, 11, + 10, 11, -265, -177, 215, 496, -175, 219, -265, 228, + 497, 662, 220, 35, 122, 9, 223, 452, 37, 234, + 249, 410, 250, 94, 228, 228, 453, 112, 164, -176, + 408, 95, 47, 48, 9, 96, 79, 647, 165, 51, + 10, 11, 496, -174, -264, 97, 98, 497, -178, 209, + -264, 277, 262, -176, 353, 163, 495, 454, 480, 623, + 482, -176, 331, 332, 10, 11, 498, -174, 349, 61, + 354, 285, -178, 616, 520, -174, 226, 515, 99, 286, + -178, 64, 358, 10, 11, 483, 360, 243, 529, 478, + 231, 231, 231, 287, 490, 364, 362, 243, 228, 111, + 368, 514, 372, 626, 231, 374, 378, 111, 254, 380, + 228, 111, 481, 231, 146, 522, 150, 631, 257, 384, + 228, 231, 382, 199, 228, 386, 266, 200, 231, 394, + 507, 150, 392, 399, 401, 201, 165, 263, 164, 405, + 413, 416, 419, 264, 228, 228, 432, 445, 446, 254, + 448, 231, 79, 79, 480, 449, 482, 10, 11, 459, + 349, 542, 447, 549, 464, 163, 467, 468, 454, 469, + 480, 470, 482, 614, 454, 471, 165, 562, 349, 486, + 379, 483, 235, 491, 255, 509, 9, 79, 254, 117, + 585, 504, 243, 256, 9, 494, 9, 483, 10, 11, + 231, 506, 231, 511, 9, 513, 521, 164, 481, 525, + 527, 434, 9, 530, 531, 228, 532, 263, 231, 534, + 231, 127, 340, 264, 481, 535, 231, 554, 556, 143, + 557, 147, 265, 564, 163, 10, 11, 10, 11, 172, + 9, 520, 661, 10, 11, 10, 11, 317, 231, 568, + 463, 570, -156, 10, 11, 573, 575, 480, 228, 482, + 412, 10, 11, 231, 231, 117, 117, 117, 210, 210, + 577, 210, 210, 578, 235, 581, 288, 592, 593, 117, + 583, 595, 596, 529, 483, 243, 254, 597, 117, 10, + 11, 79, -157, 598, 165, 600, 117, 599, 150, 602, + 603, 334, 604, 117, 606, 608, 642, 615, 228, 627, + 335, 481, 349, 630, 542, 336, 337, 338, 549, 454, + 177, 255, 339, 349, 349, 480, 117, 482, 629, 340, + 185, 641, 438, 164, 189, 10, 11, 231, 648, 194, + 195, 196, 197, 649, 650, 651, 341, 657, 106, 231, + 66, 484, 483, 628, 580, 654, 590, 591, 342, 231, + 163, 370, 123, 231, 343, 371, 345, 11, 404, 493, + 284, 505, 355, 356, 352, 117, 476, 117, 91, 481, + 443, 444, 574, 231, 231, 344, 539, 563, 637, 634, + 559, 344, 344, 117, 351, 117, 0, 0, 0, 37, + 0, 117, 0, 0, 165, 0, 0, 0, 112, 0, + 0, 0, 0, 47, 48, 9, 0, 0, 0, 0, + 51, 0, 0, 117, 0, 0, 0, 224, 0, 0, + 0, 0, 0, 0, 137, 117, 0, 0, 117, 117, + 0, 0, 175, -274, 114, 0, 160, 484, 0, 169, + 225, 0, 0, 0, 231, 0, 280, 0, 0, 0, + 0, 0, 64, 484, 10, 11, 281, 0, 0, 0, + 0, 176, 177, 165, 178, 179, 180, 181, 182, 0, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 450, 231, 0, 0, + 0, 0, 0, -274, 461, 0, 0, 0, 344, 0, + 0, 0, 117, -274, 0, 344, 0, 0, 0, 0, + 0, 0, 0, 344, 117, 0, 117, 0, 0, 0, + 0, 0, 0, 0, 117, 0, 0, 0, 117, 0, + 0, 0, 0, 0, 0, 0, 0, 231, 0, 0, + 484, 0, 0, 0, 0, 0, 0, 0, 117, 117, + 292, 293, 294, 295, 0, 296, 297, 298, 0, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 0, 160, 0, 319, 0, 322, 0, + 0, 0, 137, 137, 333, 538, 0, 0, 0, 165, + 0, 545, 548, 0, 553, 0, 0, 0, 0, 0, + 0, 0, 0, 558, 344, 560, 0, 0, 484, 0, + 543, 344, 117, 344, 0, 0, 0, 0, 0, 117, + 0, 0, 344, 0, 344, 0, 0, 0, 117, 0, + 37, 0, 0, 35, 0, 0, 0, 0, 37, 112, + 0, 168, 0, 0, 47, 48, 9, 112, 0, 0, + 0, 51, 47, 48, 9, 137, 0, 0, 224, 51, + 0, 0, 117, 0, 0, 137, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 56, + 57, 225, 58, 59, 0, 0, 60, 290, 421, 61, + 0, 0, 160, 64, 601, 10, 11, 281, 421, 62, + 63, 64, 605, 10, 11, 0, 0, 0, 37, 0, + 0, 241, 117, 344, 0, 117, 0, 112, 0, 0, + 0, 344, 47, 48, 9, 0, 0, 0, 344, 51, + 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, + 0, 137, 137, 0, 545, 635, 0, 0, 0, 0, + 0, 0, 0, 114, 0, 0, 0, 0, 0, 225, + 344, 0, 0, 543, 344, 0, 0, 0, 0, -2, + 34, 64, 35, 10, 11, 36, 0, 37, 38, 39, + 137, 0, 40, 117, 41, 42, 43, 44, 45, 46, + 0, 47, 48, 9, 137, 0, 49, 50, 51, 52, + 53, 54, 160, 0, 0, 55, 0, 169, 0, 0, + 0, 0, 344, 0, 344, 0, 0, 0, 56, 57, + 0, 58, 59, 0, 0, 60, 0, 0, 61, 0, + 0, -24, 0, 0, 0, 0, 0, 0, 62, 63, + 64, 0, 10, 11, 0, 0, 0, 0, 566, 567, + 0, 0, 0, 0, 0, 0, 0, 0, 324, 0, + 35, 0, 0, 36, -249, 37, 38, 39, 0, -249, + 40, 160, 41, 42, 112, 44, 45, 46, 0, 47, + 48, 9, 0, 0, 49, 50, 51, 52, 53, 54, + 0, 421, 0, 55, 0, 0, 0, 0, 421, 588, + 421, 0, 0, 0, 0, 0, 56, 57, 0, 58, + 59, 0, 0, 60, 0, 0, 61, 0, 0, -249, + 0, 0, 0, 0, 325, -249, 62, 63, 64, 0, + 10, 11, 324, 0, 35, 0, 0, 36, 0, 37, + 38, 39, 0, 0, 40, 0, 41, 42, 112, 44, + 45, 46, 0, 47, 48, 9, 177, 0, 49, 50, + 51, 52, 53, 54, 0, 0, 185, 55, 0, 0, 189, 190, 191, 192, 193, 194, 195, 196, 197, 0, - 0, 0, 117, 117, 292, 293, 294, 295, 0, 296, - 297, 298, 0, 299, 300, 301, 302, 303, 304, 305, - 306, 307, 308, 309, 310, 311, 312, 0, 160, 0, - 319, 0, 322, 344, 0, 0, 137, 137, 333, 0, - 344, 334, 0, 0, 0, 459, 0, 0, 344, 0, - 175, 335, 0, 0, -269, 0, 336, 337, 338, 0, - 0, 0, 0, 339, 0, 117, 0, 0, 0, 0, - 340, 0, 117, 0, 0, 0, 0, 0, 0, 176, - 177, 117, 178, 179, 180, 181, 182, 341, 183, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, - 195, 196, 197, 0, 0, 343, 0, 0, 11, 137, - 0, -269, 0, 0, 0, 117, 0, 0, 0, 137, - 0, -269, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 344, 0, - 0, 0, 421, 0, 542, 344, 160, 344, 0, 0, - 0, 0, 421, 0, -2, 34, 344, 35, 344, 36, - 0, 37, 0, 38, 39, 117, 0, 40, 117, 41, - 42, 43, 44, 45, 46, 0, 47, 48, 9, 0, - 0, 49, 50, 51, 52, 53, 54, 0, 0, 0, - 55, 0, 0, 0, 0, 137, 137, 0, 0, 0, - 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, - 60, 0, 0, 61, 0, 0, -24, 0, 35, 0, - 0, 0, 37, 62, 63, 64, 168, 10, 11, 0, - 0, 0, 112, 0, 137, 117, 0, 47, 48, 9, - 0, 0, 0, 0, 51, 0, 0, 344, 137, 0, - 0, 55, 0, 0, 0, 344, 160, 0, 0, 0, - 0, 169, 344, 0, 56, 57, 0, 58, 59, 0, - 0, 60, 0, 0, 61, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 62, 63, 64, 0, 10, 11, - 0, 0, 0, 344, 0, 0, 542, 344, 0, 0, - 0, 565, 566, 0, 0, 0, 0, 0, 0, 0, - 0, 324, 0, 35, 0, 36, -244, 37, 0, 38, - 39, 0, -244, 40, 160, 41, 42, 112, 44, 45, - 46, 0, 47, 48, 9, 0, 0, 49, 50, 51, - 52, 53, 54, 344, 421, 344, 55, 0, 0, 0, - 0, 421, 586, 421, 0, 0, 0, 0, 0, 56, - 57, 0, 58, 59, 0, 0, 60, 0, 0, 61, - 0, 0, -244, 0, 0, 0, 0, 325, -244, 62, - 63, 64, 0, 10, 11, 324, 0, 35, 0, 36, - 0, 37, 0, 38, 39, 0, 0, 40, 0, 41, - 42, 112, 44, 45, 46, 0, 47, 48, 9, 0, - 0, 49, 50, 51, 52, 53, 54, 0, 0, 0, - 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 56, 57, 0, 58, 59, 0, 0, - 60, 0, 0, 61, 0, 35, -244, 642, 643, 37, - 160, 325, -244, 62, 63, 64, 0, 10, 11, 112, - 334, 0, 0, 0, 47, 48, 9, 0, 0, 0, - 335, 51, 0, 0, 0, 336, 337, 338, 158, 0, - 0, 0, 339, 0, 0, 0, 0, 0, 0, 340, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 314, 0, 37, 0, 341, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 342, 47, - 48, 9, 0, 0, 343, 0, 51, 11, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 420, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 429, 0, 0, 0, 0, 158, 0, + 56, 57, 0, 58, 59, 0, 0, 60, 0, 0, + 61, 0, 0, -249, 645, 646, 0, 160, 325, -249, + 62, 63, 64, 35, 10, 11, 421, 0, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 112, 0, 334, + 0, 0, 47, 48, 9, 0, 0, 0, 335, 51, + 0, 0, 0, 336, 337, 338, 158, 0, 0, 0, + 339, 0, 0, 0, 0, 0, 0, 340, 0, 56, + 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, + 314, 0, 0, 37, 341, 0, 0, 0, 0, 62, + 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, + 0, 0, 343, 0, 51, 11, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, + 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, + 0, 0, 420, 0, 62, 63, 64, 112, 10, 11, + 0, 0, 47, 48, 9, 0, 0, 0, 0, 51, + 0, 429, 0, 0, 0, 0, 158, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, + 57, 0, 58, 159, 0, 0, 60, 0, 35, 61, + 0, 0, 0, 37, 0, 0, 0, 0, 0, 62, + 63, 64, 112, 10, 11, 0, 0, 47, 48, 9, + 0, 473, 0, 0, 51, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 57, 0, 58, 59, 0, + 0, 60, 0, 35, 61, 0, 0, 0, 37, 0, + 0, 0, 0, 0, 62, 63, 64, 112, 10, 11, + 0, 0, 47, 48, 9, 0, 474, 0, 0, 51, + 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 56, + 57, 37, 58, 59, 0, 0, 60, 0, 0, 61, + 112, 0, 0, 0, 0, 47, 48, 9, 0, 62, + 63, 64, 51, 10, 11, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 0, 0, 37, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 472, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 473, 0, - 0, 51, 0, 0, 0, 0, 0, 0, 55, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 59, 0, 0, 60, 0, - 35, 61, 0, 0, 37, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 35, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 0, 0, 0, 0, 0, 158, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 56, 57, 0, 58, 159, 0, 0, 60, 0, - 35, 61, 0, 0, 283, 0, 0, 0, 0, 0, - 0, 62, 63, 64, 112, 10, 11, 0, 0, 47, - 48, 9, 0, 0, 0, 0, 51, 0, 0, 0, - 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 56, 57, 0, 58, - 59, 0, 0, 60, 0, 0, 61, 0, 0, 37, - 0, 0, 0, 0, 0, 0, 62, 63, 64, 112, - 10, 11, 0, 0, 47, 48, 9, 0, 0, 0, - 0, 51, 0, 0, 0, 37, 0, 0, 224, 241, - 0, 0, 0, 37, 0, 112, 0, 0, 0, 0, - 47, 48, 9, 112, 0, 114, 0, 51, 47, 48, - 9, 225, 0, 0, 224, 51, 0, 290, 37, 0, - 0, 0, 113, 64, 0, 10, 11, 281, 112, 0, - 0, 114, 0, 47, 48, 9, 0, 225, 0, 114, - 51, 0, 0, 0, 0, 115, 37, 224, 0, 64, - 0, 10, 11, 0, 0, 0, 112, 64, 0, 10, - 11, 47, 48, 9, 114, 0, 0, 0, 51, 0, - 225, 37, 0, 0, 0, 406, 0, 0, 0, 283, - 0, 112, 64, 0, 10, 11, 47, 48, 9, 112, - 0, 0, 114, 51, 47, 48, 9, 0, 407, 0, - 224, 51, 0, 0, 334, 0, 0, 0, 224, 0, - 64, 0, 10, 11, 335, 0, 0, 114, 0, 336, - 337, 338, 0, 476, 0, 114, 339, 0, 0, 0, - 0, 225, 334, 439, 0, 64, 0, 10, 11, 0, - 0, 0, 335, 64, 0, 10, 11, 336, 337, 540, - 341, 0, 0, 0, 339, 0, 440, 334, 0, 0, - 0, 340, 0, 0, 0, 334, 0, 335, 343, 0, - 0, 11, 336, 337, 338, 335, 0, 0, 341, 339, - 336, 337, 338, 0, 0, 0, 340, 339, 0, 0, - 0, 0, 0, 0, 340, 0, 343, 0, 10, 11, - 0, 0, 0, 341, 0, 0, 0, 0, 0, 607, - 0, 341, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 343, 0, 0, 11, 0, 0, 0, 0, 343, - 176, 177, 11, 178, 0, 180, 181, 182, 0, 0, - 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 0, 0, 0, 0, 0, 0, - 176, 177, 0, 178, 0, 180, 181, 182, 0, 431, - 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, - 194, 195, 196, 197, 176, 177, 0, 178, 0, 180, - 181, 182, 0, 523, 184, 185, 186, 187, 188, 189, - 190, 191, 192, 193, 194, 195, 196, 197, 176, 177, - 0, 178, 0, 180, 181, 182, 0, 649, 184, 185, + 0, 0, 56, 57, 0, 58, 59, 0, 0, 60, + 0, 35, 61, 0, 0, 0, 37, 0, 0, 0, + 587, 0, 62, 63, 64, 112, 10, 11, 0, 0, + 47, 48, 9, 0, 0, 0, 0, 51, 0, 0, + 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 56, 57, 37, + 58, 59, 0, 0, 60, 0, 0, 61, 112, 0, + 0, 0, 0, 47, 48, 9, 0, 62, 63, 64, + 51, 10, 11, 0, 0, 0, 0, 158, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 56, 57, 283, 58, 159, 0, 0, 60, 0, 0, + 61, 112, 0, 0, 0, 0, 47, 48, 9, 0, + 62, 63, 64, 51, 10, 11, 0, 0, 0, 0, + 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 56, 57, 37, 58, 59, 0, 0, + 60, 0, 0, 61, 112, 0, 0, 0, 0, 47, + 48, 9, 0, 62, 63, 64, 51, 10, 11, 0, + 37, 0, 0, 224, 0, 0, 0, 0, 37, 112, + 0, 0, 0, 0, 47, 48, 9, 112, 0, 0, + 114, 51, 47, 48, 9, 0, 225, 0, 113, 51, + 0, 0, 0, 0, 37, 0, 224, 0, 64, 0, + 10, 11, 396, 112, 0, 114, 0, 0, 47, 48, + 9, 115, 0, 114, 0, 51, 0, 0, 0, 225, + 0, 37, 406, 64, 0, 10, 11, 0, 0, 0, + 112, 64, 0, 10, 11, 47, 48, 9, 0, 114, + 0, 0, 51, 0, 0, 407, 0, 283, 0, 224, + 0, 0, 0, 0, 0, 334, 112, 64, 0, 10, + 11, 47, 48, 9, 335, 0, 114, 0, 51, 336, + 337, 338, 477, 0, 0, 224, 339, 0, 0, 0, + 334, 0, 0, 440, 64, 0, 10, 11, 334, 335, + 0, 460, 114, 0, 336, 337, 541, 335, 225, 0, + 341, 339, 336, 337, 338, 0, 441, 0, 340, 339, + 64, 0, 10, 11, 334, 0, 340, 0, 343, 0, + 0, 11, 0, 335, 0, 341, 0, 0, 336, 337, + 338, 0, 0, 341, 0, 339, 0, 0, 0, 0, + 0, 0, 340, 343, 0, 10, 11, 0, 0, 0, + 0, 343, 177, 0, 11, 0, 180, 181, 182, 341, + 0, 184, 185, 186, 187, 609, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 0, 343, 176, 177, + 11, 178, 0, 180, 181, 182, 0, 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, - 196, 197, 176, 177, 0, 178, 0, 180, 181, 182, - 0, 650, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 195, 196, 197, 0, 176, 177, 434, - 178, 0, 180, 181, 182, 0, 0, 184, 185, 186, + 196, 197, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, + 434, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 0, 180, 181, 182, 0, + 431, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 176, 177, 0, 178, 0, + 180, 181, 182, 0, 524, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 176, + 177, 0, 178, 0, 180, 181, 182, 0, 655, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 176, 177, 0, 178, 0, 180, 181, + 182, 0, 656, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 176, 177, 0, + 0, 0, 180, 181, 182, 0, 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 176, 177, 0, 0, 0, 180, 181, 182, 0, - 0, 184, 185, 186, 187, 188, 189, 190, 191, 192, - 193, 194, 195, 196, 197, 176, 177, 0, 0, 0, - 180, 181, 182, 0, 0, 184, 185, 186, 187, 0, - 189, 190, 191, 192, 193, 194, 195, 196, 197, 177, - 0, 0, 0, 180, 181, 182, 0, 0, 184, 185, - 186, 187, 0, 189, 190, 191, 192, 193, 194, 195, - 196, 197 + 0, 184, 185, 186, 187, 0, 189, 190, 191, 192, + 193, 194, 195, 196, 197 }; -#define yypact_value_is_default(yystate) \ - ((yystate) == (-551)) - -#define yytable_value_is_error(yytable_value) \ - YYID (0) - static const yytype_int16 yycheck[] = { - 5, 67, 250, 223, 37, 126, 37, 126, 143, 135, - 201, 132, 204, 147, 61, 20, 36, 22, 485, 39, - 31, 142, 28, 28, 321, 45, 452, 316, 323, 249, - 258, 36, 3, 11, 39, 24, 263, 264, 43, 267, - 45, 444, 67, 61, 594, 35, 0, 275, 53, 54, - 6, 279, 1, 3, 1, 3, 12, 3, 24, 35, - 59, 289, 67, 95, 63, 97, 98, 49, 3, 59, - 59, 35, 71, 25, 24, 37, 24, 67, 24, 3, - 37, 6, 24, 59, 389, 40, 35, 12, 35, 44, - 24, 67, 397, 59, 644, 106, 646, 75, 20, 61, - 24, 72, 50, 67, 61, 572, 172, 73, 74, 59, - 59, 59, 68, 63, 60, 135, 8, 63, 67, 53, - 67, 141, 127, 73, 74, 73, 74, 73, 74, 255, - 135, 73, 74, 8, 62, 424, 141, 426, 143, 63, - 437, 159, 147, 68, 126, 67, 25, 172, 62, 73, - 74, 113, 114, 115, 68, 460, 113, 114, 115, 24, - 6, 388, 382, 390, 3, 127, 12, 172, 21, 59, - 127, 62, 639, 65, 136, 63, 602, 68, 60, 136, - 24, 199, 144, 75, 74, 24, 8, 144, 436, 151, - 65, 66, 595, 75, 151, 200, 62, 159, 503, 62, - 75, 206, 159, 606, 607, 68, 35, 212, 113, 114, - 115, 50, 174, 62, 220, 220, 62, 174, 223, 68, - 515, 413, 68, 471, 62, 207, 208, 522, 420, 234, - 59, 136, 40, 24, 73, 74, 44, 199, 67, 144, - 3, 432, 199, 65, 249, 250, 151, 368, 3, 368, - 283, 51, 283, 75, 559, 71, 476, 378, 384, 378, - 62, 223, 67, 225, 399, 66, 223, 401, 225, 174, - 317, 576, 577, 66, 3, 59, 281, 523, 524, 241, - 59, 243, 24, 83, 241, 59, 243, 249, 74, 89, - 518, 3, 249, 24, 599, 7, 6, 64, 280, 317, - 62, 59, 12, 63, 75, 17, 59, 35, 290, 271, - 22, 23, 24, 67, 271, 60, 321, 29, 60, 59, - 225, 283, 35, 72, 286, 287, 283, 575, 72, 286, - 287, 73, 74, 62, 63, 3, 241, 342, 211, 68, - 24, 374, 60, 374, 217, 218, 59, 59, 353, 569, - 416, 7, 62, 60, 67, 317, 374, 24, 68, 71, - 317, 73, 74, 35, 384, 62, 271, 372, 60, 374, - 75, 62, 392, 35, 379, 59, 396, 382, 283, 384, - 427, 286, 287, 37, 366, 367, 53, 392, 24, 73, - 74, 396, 59, 59, 399, 35, 401, 59, 360, 62, - 3, 68, 72, 360, 62, 67, 73, 74, 65, 427, - 372, 416, 374, 62, 62, 372, 67, 374, 62, 59, - 382, 66, 65, 405, 386, 382, 59, 67, 67, 386, - 71, 436, 437, 67, 7, 65, 469, 419, 469, 444, - 445, 62, 447, 24, 406, 407, 567, 452, 567, 406, - 407, 469, 485, 458, 485, 360, 461, 462, 62, 113, - 114, 115, 62, 62, 60, 427, 471, 485, 341, 60, - 427, 476, 9, 127, 60, 24, 349, 495, 59, 63, - 17, 386, 136, 60, 21, 68, 24, 68, 60, 68, - 144, 35, 73, 74, 31, 32, 60, 151, 60, 60, - 75, 406, 407, 68, 53, 75, 60, 469, 60, 24, - 59, 68, 469, 60, 476, 36, 582, 3, 60, 476, - 174, 59, 62, 485, 73, 74, 72, 64, 485, 62, - 24, 59, 24, 495, 75, 73, 74, 60, 495, 572, - 66, 572, 60, 94, 95, 60, 97, 98, 55, 56, - 57, 58, 59, 60, 572, 62, 63, 519, 73, 74, - 60, 60, 519, 60, 569, 59, 439, 62, 60, 223, - 575, 225, 445, 446, 621, 448, 24, 582, 60, 73, - 74, 73, 74, 72, 457, 24, 459, 241, 60, 243, - 595, 59, 597, 59, 59, 249, 601, 602, 68, 62, - 62, 606, 607, 621, 68, 72, 639, 569, 639, 49, - 572, 59, 569, 62, 519, 572, 59, 271, 60, 14, - 59, 639, 68, 60, 68, 73, 74, 34, 49, 283, - 60, 62, 286, 287, 73, 74, 60, 44, 60, 31, - 61, 48, 60, 64, 60, 22, 53, 54, 55, 56, - 582, 158, 159, 508, 615, 377, 615, 142, 243, 621, - 211, 281, 524, 7, 621, 39, 217, 218, 159, 243, - 213, 7, 392, 17, 372, 548, 384, 639, 22, 23, - 24, 17, 639, 556, 22, 29, 22, 23, 24, 495, - 440, 601, 36, 29, 462, 597, 334, 334, 212, 458, - 36, -1, -1, -1, -1, -1, 360, -1, -1, 53, - -1, -1, -1, -1, -1, 59, -1, 53, 372, -1, - 374, 65, -1, 59, 597, 598, 34, 71, 382, 73, - 74, 75, 386, -1, -1, 71, 44, 73, 74, 75, + 5, 37, 61, 37, 142, 67, 223, 377, 143, 323, + 389, 126, 258, 250, 204, 20, 147, 22, 397, 135, + 126, 267, 28, 28, 201, 49, 132, 453, 31, 275, + 445, 36, 249, 279, 39, 321, 142, 3, 43, 67, + 45, 486, 11, 289, 5, 263, 264, 24, 53, 54, + 36, 316, 61, 39, 3, 37, 24, 322, 24, 45, + 25, 1, 67, 113, 114, 115, 20, 0, 24, 59, + 55, 56, 57, 58, 59, 60, 53, 62, 63, 61, + 25, 5, 461, 5, 74, 24, 136, 95, 24, 97, + 98, 59, 1, 59, 144, 35, 60, 63, 21, 3, + 62, 151, 126, 106, 65, 66, 75, 73, 74, 24, + 172, 75, 596, 67, 75, 35, 3, 73, 74, 59, + 3, 60, 127, 72, 174, 504, 35, 67, 573, 63, + 135, 113, 114, 115, 73, 74, 141, 24, 143, 255, + 71, 65, 147, 65, 172, 127, 62, 67, 62, 135, + 159, 75, 438, 75, 136, 141, 40, 62, 67, 424, + 44, 426, 144, 68, 648, 382, 650, 172, 3, 151, + 388, 24, 390, 158, 159, 225, 63, 159, 604, 62, + 63, 560, 597, 207, 208, 68, 73, 74, 51, 24, + 199, 241, 174, 608, 609, 200, 641, 62, 577, 578, + 437, 206, 516, 68, 40, 62, 62, 212, 44, 523, + 3, 68, 68, 3, 220, 220, 35, 199, 223, 62, + 83, 271, 601, 413, 3, 60, 89, 7, 63, 234, + 420, 24, 12, 283, 67, 472, 286, 287, 73, 74, + 59, 223, 66, 225, 249, 250, 66, 283, 67, 283, + 3, 35, 59, 368, 35, 432, 280, 50, 317, 241, + 477, 243, 368, 378, 399, 59, 290, 249, 384, 7, + 401, 24, 378, 519, 12, 59, 281, 59, 59, 74, + 73, 74, 62, 67, 24, 7, 67, 64, 68, 271, + 12, 661, 62, 3, 37, 24, 59, 50, 8, 63, + 59, 283, 67, 9, 286, 287, 59, 17, 317, 35, + 360, 17, 22, 23, 24, 21, 321, 631, 61, 29, + 73, 74, 7, 35, 62, 31, 32, 12, 35, 75, + 68, 60, 35, 59, 59, 317, 386, 342, 374, 576, + 374, 67, 366, 367, 73, 74, 68, 59, 353, 59, + 3, 60, 59, 570, 416, 67, 406, 407, 64, 72, + 67, 71, 60, 73, 74, 374, 8, 372, 427, 374, + 113, 114, 115, 72, 379, 35, 60, 382, 360, 384, + 62, 405, 75, 68, 127, 60, 62, 392, 24, 24, + 372, 396, 374, 136, 399, 419, 401, 587, 384, 62, + 382, 144, 59, 59, 386, 72, 392, 63, 151, 3, + 396, 416, 62, 62, 62, 71, 159, 53, 427, 65, + 67, 62, 65, 59, 406, 407, 66, 59, 67, 24, + 67, 174, 437, 438, 470, 8, 470, 73, 74, 65, + 445, 446, 71, 448, 62, 427, 62, 62, 453, 62, + 486, 60, 486, 568, 459, 60, 199, 462, 463, 60, + 63, 470, 568, 60, 59, 35, 24, 472, 24, 37, + 520, 60, 477, 68, 24, 68, 24, 486, 73, 74, + 223, 68, 225, 60, 24, 60, 60, 496, 470, 75, + 68, 75, 24, 60, 60, 477, 60, 53, 241, 60, + 243, 59, 36, 59, 486, 68, 249, 3, 62, 59, + 72, 59, 68, 60, 496, 73, 74, 73, 74, 59, + 24, 583, 660, 73, 74, 73, 74, 59, 271, 62, + 75, 59, 59, 73, 74, 60, 66, 573, 520, 573, + 283, 73, 74, 286, 287, 113, 114, 115, 94, 95, + 60, 97, 98, 60, 660, 60, 60, 60, 72, 127, + 62, 60, 60, 622, 573, 570, 24, 59, 136, 73, + 74, 576, 59, 68, 317, 72, 144, 62, 583, 68, + 62, 8, 49, 151, 62, 59, 14, 60, 570, 68, + 17, 573, 597, 68, 599, 22, 23, 24, 603, 604, + 34, 59, 29, 608, 609, 641, 174, 641, 60, 36, + 44, 60, 62, 622, 48, 73, 74, 360, 60, 53, + 54, 55, 56, 60, 60, 60, 53, 68, 31, 372, + 22, 374, 641, 583, 509, 642, 525, 525, 65, 382, + 622, 243, 39, 386, 71, 243, 211, 74, 281, 384, + 159, 392, 217, 218, 213, 223, 372, 225, 22, 641, + 334, 334, 496, 406, 407, 211, 441, 463, 603, 599, + 459, 217, 218, 241, 212, 243, -1, -1, -1, 8, + -1, 249, -1, -1, 427, -1, -1, -1, 17, -1, + -1, -1, -1, 22, 23, 24, -1, -1, -1, -1, + 29, -1, -1, 271, -1, -1, -1, 36, -1, -1, + -1, -1, -1, -1, 49, 283, -1, -1, 286, 287, + -1, -1, 4, 5, 53, -1, 61, 470, -1, 64, + 59, -1, -1, -1, 477, -1, 65, -1, -1, -1, + -1, -1, 71, 486, 73, 74, 75, -1, -1, -1, + -1, 33, 34, 496, 36, 37, 38, 39, 40, -1, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, 341, 520, -1, -1, + -1, -1, -1, 65, 349, -1, -1, -1, 334, -1, + -1, -1, 360, 75, -1, 341, -1, -1, -1, -1, + -1, -1, -1, 349, 372, -1, 374, -1, -1, -1, + -1, -1, -1, -1, 382, -1, -1, -1, 386, -1, + -1, -1, -1, -1, -1, -1, -1, 570, -1, -1, + 573, -1, -1, -1, -1, -1, -1, -1, 406, 407, + 175, 176, 177, 178, -1, 180, 181, 182, -1, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, -1, 199, -1, 201, -1, 203, -1, + -1, -1, 207, 208, 209, 440, -1, -1, -1, 622, + -1, 446, 447, -1, 449, -1, -1, -1, -1, -1, + -1, -1, -1, 458, 440, 460, -1, -1, 641, -1, + 446, 447, 470, 449, -1, -1, -1, -1, -1, 477, + -1, -1, 458, -1, 460, -1, -1, -1, 486, -1, + 8, -1, -1, 3, -1, -1, -1, -1, 8, 17, + -1, 11, -1, -1, 22, 23, 24, 17, -1, -1, + -1, 29, 22, 23, 24, 280, -1, -1, 36, 29, + -1, -1, 520, -1, -1, 290, 36, -1, -1, -1, + -1, -1, -1, -1, -1, 53, -1, -1, -1, 49, + 50, 59, 52, 53, -1, -1, 56, 65, 313, 59, + -1, -1, 317, 71, 549, 73, 74, 75, 323, 69, + 70, 71, 557, 73, 74, -1, -1, -1, 8, -1, + -1, 11, 570, 549, -1, 573, -1, 17, -1, -1, + -1, 557, 22, 23, 24, -1, -1, -1, 564, 29, + -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, + -1, 366, 367, -1, 599, 600, -1, -1, -1, -1, + -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, + 596, -1, -1, 599, 600, -1, -1, -1, -1, 0, + 1, 71, 3, 73, 74, 6, -1, 8, 9, 10, + 405, -1, 13, 641, 15, 16, 17, 18, 19, 20, + -1, 22, 23, 24, 419, -1, 27, 28, 29, 30, + 31, 32, 427, -1, -1, 36, -1, 432, -1, -1, + -1, -1, 648, -1, 650, -1, -1, -1, 49, 50, + -1, 52, 53, -1, -1, 56, -1, -1, 59, -1, + -1, 62, -1, -1, -1, -1, -1, -1, 69, 70, + 71, -1, 73, 74, -1, -1, -1, -1, 473, 474, + -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, + 3, -1, -1, 6, 7, 8, 9, 10, -1, 12, + 13, 496, 15, 16, 17, 18, 19, 20, -1, 22, + 23, 24, -1, -1, 27, 28, 29, 30, 31, 32, + -1, 516, -1, 36, -1, -1, -1, -1, 523, 524, + 525, -1, -1, -1, -1, -1, 49, 50, -1, 52, + 53, -1, -1, 56, -1, -1, 59, -1, -1, 62, + -1, -1, -1, -1, 67, 68, 69, 70, 71, -1, + 73, 74, 1, -1, 3, -1, -1, 6, -1, 8, + 9, 10, -1, -1, 13, -1, 15, 16, 17, 18, + 19, 20, -1, 22, 23, 24, 34, -1, 27, 28, + 29, 30, 31, 32, -1, -1, 44, 36, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, 56, -1, - -1, -1, 406, 407, 175, 176, 177, 178, -1, 180, - 181, 182, -1, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 195, 196, 197, -1, 199, -1, - 201, -1, 203, 334, -1, -1, 207, 208, 209, -1, - 341, 7, -1, -1, -1, 11, -1, -1, 349, -1, - 4, 17, -1, -1, 8, -1, 22, 23, 24, -1, - -1, -1, -1, 29, -1, 469, -1, -1, -1, -1, - 36, -1, 476, -1, -1, -1, -1, -1, -1, 33, - 34, 485, 36, 37, 38, 39, 40, 53, 42, 43, - 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, -1, -1, 71, -1, -1, 74, 280, - -1, 65, -1, -1, -1, 519, -1, -1, -1, 290, - -1, 75, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 439, -1, - -1, -1, 313, -1, 445, 446, 317, 448, -1, -1, - -1, -1, 323, -1, 0, 1, 457, 3, 459, 5, - -1, 7, -1, 9, 10, 569, -1, 13, 572, 15, - 16, 17, 18, 19, 20, -1, 22, 23, 24, -1, - -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, - 36, -1, -1, -1, -1, 366, 367, -1, -1, -1, - -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, - 56, -1, -1, 59, -1, -1, 62, -1, 3, -1, - -1, -1, 7, 69, 70, 71, 11, 73, 74, -1, - -1, -1, 17, -1, 405, 639, -1, 22, 23, 24, - -1, -1, -1, -1, 29, -1, -1, 548, 419, -1, - -1, 36, -1, -1, -1, 556, 427, -1, -1, -1, - -1, 432, 563, -1, 49, 50, -1, 52, 53, -1, - -1, 56, -1, -1, 59, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 69, 70, 71, -1, 73, 74, - -1, -1, -1, 594, -1, -1, 597, 598, -1, -1, - -1, 472, 473, -1, -1, -1, -1, -1, -1, -1, - -1, 1, -1, 3, -1, 5, 6, 7, -1, 9, - 10, -1, 12, 13, 495, 15, 16, 17, 18, 19, - 20, -1, 22, 23, 24, -1, -1, 27, 28, 29, - 30, 31, 32, 644, 515, 646, 36, -1, -1, -1, - -1, 522, 523, 524, -1, -1, -1, -1, -1, 49, - 50, -1, 52, 53, -1, -1, 56, -1, -1, 59, - -1, -1, 62, -1, -1, -1, -1, 67, 68, 69, - 70, 71, -1, 73, 74, 1, -1, 3, -1, 5, - -1, 7, -1, 9, 10, -1, -1, 13, -1, 15, - 16, 17, 18, 19, 20, -1, 22, 23, 24, -1, - -1, 27, 28, 29, 30, 31, 32, -1, -1, -1, - 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 49, 50, -1, 52, 53, -1, -1, - 56, -1, -1, 59, -1, 3, 62, 618, 619, 7, - 621, 67, 68, 69, 70, 71, -1, 73, 74, 17, - 7, -1, -1, -1, 22, 23, 24, -1, -1, -1, - 17, 29, -1, -1, -1, 22, 23, 24, 36, -1, - -1, -1, 29, -1, -1, -1, -1, -1, -1, 36, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, 60, -1, 7, -1, 53, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, 65, 22, - 23, 24, -1, -1, 71, -1, 29, 74, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, 67, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, 31, -1, -1, -1, -1, 36, -1, + 49, 50, -1, 52, 53, -1, -1, 56, -1, -1, + 59, -1, -1, 62, 619, 620, -1, 622, 67, 68, + 69, 70, 71, 3, 73, 74, 631, -1, 8, -1, + -1, -1, -1, -1, -1, -1, -1, 17, -1, 8, + -1, -1, 22, 23, 24, -1, -1, -1, 17, 29, + -1, -1, -1, 22, 23, 24, 36, -1, -1, -1, + 29, -1, -1, -1, -1, -1, -1, 36, -1, 49, + 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, + 60, -1, -1, 8, 53, -1, -1, -1, -1, 69, + 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, + -1, -1, 71, -1, 29, 74, -1, -1, -1, -1, + -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, + -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, + -1, -1, 67, -1, 69, 70, 71, 17, 73, 74, + -1, -1, 22, 23, 24, -1, -1, -1, -1, 29, + -1, 31, -1, -1, -1, -1, 36, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 49, + 50, -1, 52, 53, -1, -1, 56, -1, 3, 59, + -1, -1, -1, 8, -1, -1, -1, -1, -1, 69, + 70, 71, 17, 73, 74, -1, -1, 22, 23, 24, + -1, 26, -1, -1, 29, -1, -1, -1, -1, -1, + -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 49, 50, -1, 52, 53, -1, + -1, 56, -1, 3, 59, -1, -1, -1, 8, -1, + -1, -1, -1, -1, 69, 70, 71, 17, 73, 74, + -1, -1, 22, 23, 24, -1, 26, -1, -1, 29, + -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 3, -1, -1, 49, + 50, 8, 52, 53, -1, -1, 56, -1, -1, 59, + 17, -1, -1, -1, -1, 22, 23, 24, -1, 69, + 70, 71, 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, 26, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, 26, -1, - -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, -1, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, 3, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, -1, -1, -1, -1, -1, 36, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 49, 50, -1, 52, 53, -1, -1, 56, -1, - 3, 59, -1, -1, 7, -1, -1, -1, -1, -1, - -1, 69, 70, 71, 17, 73, 74, -1, -1, 22, - 23, 24, -1, -1, -1, -1, 29, -1, -1, -1, - -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 49, 50, -1, 52, - 53, -1, -1, 56, -1, -1, 59, -1, -1, 7, - -1, -1, -1, -1, -1, -1, 69, 70, 71, 17, - 73, 74, -1, -1, 22, 23, 24, -1, -1, -1, - -1, 29, -1, -1, -1, 7, -1, -1, 36, 11, - -1, -1, -1, 7, -1, 17, -1, -1, -1, -1, - 22, 23, 24, 17, -1, 53, -1, 29, 22, 23, - 24, 59, -1, -1, 36, 29, -1, 65, 7, -1, - -1, -1, 36, 71, -1, 73, 74, 75, 17, -1, - -1, 53, -1, 22, 23, 24, -1, 59, -1, 53, - 29, -1, -1, -1, -1, 59, 7, 36, -1, 71, - -1, 73, 74, -1, -1, -1, 17, 71, -1, 73, - 74, 22, 23, 24, 53, -1, -1, -1, 29, -1, - 59, 7, -1, -1, -1, 36, -1, -1, -1, 7, - -1, 17, 71, -1, 73, 74, 22, 23, 24, 17, - -1, -1, 53, 29, 22, 23, 24, -1, 59, -1, - 36, 29, -1, -1, 7, -1, -1, -1, 36, -1, - 71, -1, 73, 74, 17, -1, -1, 53, -1, 22, - 23, 24, -1, 59, -1, 53, 29, -1, -1, -1, - -1, 59, 7, 36, -1, 71, -1, 73, 74, -1, - -1, -1, 17, 71, -1, 73, 74, 22, 23, 24, - 53, -1, -1, -1, 29, -1, 59, 7, -1, -1, - -1, 36, -1, -1, -1, 7, -1, 17, 71, -1, - -1, 74, 22, 23, 24, 17, -1, -1, 53, 29, - 22, 23, 24, -1, -1, -1, 36, 29, -1, -1, - -1, -1, -1, -1, 36, -1, 71, -1, 73, 74, - -1, -1, -1, 53, -1, -1, -1, -1, -1, 59, - -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 71, -1, -1, 74, -1, -1, -1, -1, 71, - 33, 34, 74, 36, -1, 38, 39, 40, -1, -1, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 55, 56, -1, -1, -1, -1, -1, -1, - 33, 34, -1, 36, -1, 38, 39, 40, -1, 72, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, - 53, 54, 55, 56, 33, 34, -1, 36, -1, 38, - 39, 40, -1, 66, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, - -1, 36, -1, 38, 39, 40, -1, 66, 43, 44, + -1, -1, 49, 50, -1, 52, 53, -1, -1, 56, + -1, 3, 59, -1, -1, -1, 8, -1, -1, -1, + 67, -1, 69, 70, 71, 17, 73, 74, -1, -1, + 22, 23, 24, -1, -1, -1, -1, 29, -1, -1, + -1, -1, -1, -1, 36, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 3, -1, -1, 49, 50, 8, + 52, 53, -1, -1, 56, -1, -1, 59, 17, -1, + -1, -1, -1, 22, 23, 24, -1, 69, 70, 71, + 29, 73, 74, -1, -1, -1, -1, 36, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, + 49, 50, 8, 52, 53, -1, -1, 56, -1, -1, + 59, 17, -1, -1, -1, -1, 22, 23, 24, -1, + 69, 70, 71, 29, 73, 74, -1, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 49, 50, 8, 52, 53, -1, -1, + 56, -1, -1, 59, 17, -1, -1, -1, -1, 22, + 23, 24, -1, 69, 70, 71, 29, 73, 74, -1, + 8, -1, -1, 36, -1, -1, -1, -1, 8, 17, + -1, -1, -1, -1, 22, 23, 24, 17, -1, -1, + 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, + -1, -1, -1, -1, 8, -1, 36, -1, 71, -1, + 73, 74, 75, 17, -1, 53, -1, -1, 22, 23, + 24, 59, -1, 53, -1, 29, -1, -1, -1, 59, + -1, 8, 36, 71, -1, 73, 74, -1, -1, -1, + 17, 71, -1, 73, 74, 22, 23, 24, -1, 53, + -1, -1, 29, -1, -1, 59, -1, 8, -1, 36, + -1, -1, -1, -1, -1, 8, 17, 71, -1, 73, + 74, 22, 23, 24, 17, -1, 53, -1, 29, 22, + 23, 24, 59, -1, -1, 36, 29, -1, -1, -1, + 8, -1, -1, 36, 71, -1, 73, 74, 8, 17, + -1, 11, 53, -1, 22, 23, 24, 17, 59, -1, + 53, 29, 22, 23, 24, -1, 59, -1, 36, 29, + 71, -1, 73, 74, 8, -1, 36, -1, 71, -1, + -1, 74, -1, 17, -1, 53, -1, -1, 22, 23, + 24, -1, -1, 53, -1, 29, -1, -1, -1, -1, + -1, -1, 36, 71, -1, 73, 74, -1, -1, -1, + -1, 71, 34, -1, 74, -1, 38, 39, 40, 53, + -1, 43, 44, 45, 46, 59, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, -1, 71, 33, 34, + 74, 36, -1, 38, 39, 40, -1, -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 33, 34, -1, 36, -1, 38, 39, 40, - -1, 66, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, -1, 33, 34, 60, - 36, -1, 38, 39, 40, -1, -1, 43, 44, 45, + 55, 56, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, + 75, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, 38, 39, 40, -1, + 72, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 33, 34, -1, 36, -1, + 38, 39, 40, -1, 66, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, + 34, -1, 36, -1, 38, 39, 40, -1, 66, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 33, 34, -1, 36, -1, 38, 39, + 40, -1, 66, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, + -1, -1, 38, 39, 40, -1, -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 33, 34, -1, -1, -1, 38, 39, 40, -1, - -1, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 54, 55, 56, 33, 34, -1, -1, -1, - 38, 39, 40, -1, -1, 43, 44, 45, 46, -1, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 34, - -1, -1, -1, 38, 39, 40, -1, -1, 43, 44, - 45, 46, -1, 48, 49, 50, 51, 52, 53, 54, - 55, 56 + -1, 43, 44, 45, 46, -1, 48, 49, 50, 51, + 52, 53, 54, 55, 56 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -1488,71 +1485,72 @@ static const yytype_int16 yycheck[] = static const yytype_uint8 yystos[] = { 0, 77, 79, 80, 0, 25, 78, 25, 86, 24, - 73, 74, 137, 138, 81, 24, 88, 89, 3, 62, - 21, 82, 162, 24, 87, 210, 63, 3, 59, 63, - 83, 85, 137, 62, 1, 3, 5, 7, 9, 10, + 73, 74, 141, 142, 81, 24, 88, 89, 3, 62, + 21, 82, 166, 24, 87, 214, 63, 3, 59, 63, + 83, 85, 141, 62, 1, 3, 6, 8, 9, 10, 13, 15, 16, 17, 18, 19, 20, 22, 23, 27, 28, 29, 30, 31, 32, 36, 49, 50, 52, 53, 56, 59, 69, 70, 71, 90, 91, 92, 98, 110, - 113, 118, 121, 123, 124, 125, 126, 130, 134, 137, - 139, 140, 145, 146, 149, 152, 153, 154, 157, 160, - 161, 177, 182, 62, 9, 17, 21, 31, 32, 64, - 195, 24, 60, 83, 84, 3, 86, 88, 3, 134, - 136, 137, 17, 36, 53, 59, 137, 139, 144, 148, - 149, 150, 157, 136, 125, 130, 111, 59, 137, 155, - 125, 134, 114, 35, 67, 133, 71, 123, 182, 189, - 122, 133, 119, 59, 96, 97, 137, 59, 93, 135, - 137, 181, 124, 124, 124, 124, 124, 124, 36, 53, - 123, 131, 143, 149, 151, 157, 124, 124, 11, 123, - 188, 62, 59, 94, 181, 4, 33, 34, 36, 37, + 113, 121, 124, 126, 127, 128, 129, 134, 138, 141, + 143, 144, 149, 150, 153, 156, 157, 158, 161, 164, + 165, 181, 186, 62, 9, 17, 21, 31, 32, 64, + 199, 24, 60, 83, 84, 3, 86, 88, 3, 138, + 140, 141, 17, 36, 53, 59, 141, 143, 148, 152, + 153, 154, 161, 140, 128, 134, 111, 59, 141, 159, + 128, 138, 114, 35, 67, 137, 71, 126, 186, 193, + 125, 137, 122, 59, 96, 97, 141, 59, 93, 139, + 141, 185, 127, 127, 127, 127, 127, 127, 36, 53, + 126, 135, 147, 153, 155, 161, 127, 127, 11, 126, + 192, 62, 59, 94, 185, 4, 33, 34, 36, 37, 38, 39, 40, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 67, 59, - 63, 71, 66, 59, 133, 1, 133, 8, 65, 75, - 138, 196, 59, 156, 196, 24, 196, 197, 196, 64, - 62, 186, 88, 59, 36, 59, 142, 148, 149, 150, - 151, 157, 142, 142, 63, 98, 107, 108, 109, 182, - 190, 11, 132, 137, 141, 142, 173, 174, 175, 59, - 67, 158, 112, 190, 24, 59, 68, 134, 167, 169, - 171, 142, 35, 53, 59, 68, 134, 166, 168, 169, - 170, 180, 112, 60, 97, 165, 142, 60, 93, 163, - 65, 75, 142, 7, 143, 60, 72, 72, 60, 94, - 65, 142, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, - 123, 123, 123, 127, 60, 131, 183, 59, 137, 123, - 188, 178, 123, 127, 1, 67, 91, 100, 176, 177, - 179, 182, 182, 123, 7, 17, 22, 23, 24, 29, - 36, 53, 65, 71, 138, 198, 200, 201, 202, 137, - 203, 211, 158, 59, 3, 198, 198, 83, 60, 175, - 7, 142, 60, 137, 35, 105, 8, 65, 62, 142, - 132, 141, 75, 187, 60, 175, 179, 115, 62, 63, - 24, 169, 59, 172, 62, 186, 72, 104, 59, 170, - 53, 170, 62, 186, 3, 194, 75, 142, 120, 62, - 186, 62, 186, 182, 135, 65, 36, 59, 142, 148, - 149, 150, 157, 67, 142, 142, 62, 186, 182, 65, - 67, 123, 128, 129, 184, 185, 11, 75, 187, 31, - 131, 72, 66, 176, 60, 185, 101, 62, 68, 36, - 59, 199, 200, 202, 59, 67, 71, 67, 7, 198, - 3, 50, 59, 137, 208, 209, 3, 72, 65, 11, - 198, 60, 75, 62, 191, 211, 62, 62, 62, 60, - 60, 106, 26, 26, 190, 173, 59, 137, 147, 148, - 149, 150, 151, 157, 159, 60, 68, 105, 190, 137, - 60, 175, 171, 68, 142, 6, 12, 68, 99, 102, - 170, 194, 170, 60, 168, 68, 134, 194, 35, 97, - 60, 93, 60, 182, 142, 127, 94, 95, 164, 181, - 60, 182, 127, 66, 75, 187, 68, 75, 187, 131, - 60, 60, 60, 188, 68, 179, 176, 198, 201, 191, - 24, 137, 138, 193, 198, 205, 213, 198, 137, 192, - 204, 212, 198, 3, 208, 62, 72, 198, 209, 198, - 194, 137, 203, 60, 179, 123, 123, 62, 175, 59, - 159, 116, 60, 183, 66, 103, 60, 60, 194, 104, - 60, 185, 62, 186, 142, 185, 123, 129, 128, 129, - 60, 72, 68, 60, 60, 59, 68, 62, 72, 198, - 68, 62, 49, 198, 62, 194, 59, 59, 198, 206, - 207, 68, 190, 60, 175, 14, 117, 159, 8, 65, - 66, 75, 179, 194, 194, 68, 68, 95, 60, 68, - 206, 191, 205, 198, 194, 204, 208, 191, 191, 60, - 100, 113, 123, 123, 60, 60, 60, 60, 159, 66, - 66, 206, 206 + 63, 71, 66, 59, 137, 1, 137, 5, 65, 75, + 142, 200, 59, 160, 200, 24, 200, 201, 200, 64, + 62, 190, 88, 59, 36, 59, 146, 152, 153, 154, + 155, 161, 146, 146, 63, 98, 107, 108, 109, 186, + 194, 11, 136, 141, 145, 146, 177, 178, 179, 59, + 67, 162, 112, 194, 24, 59, 68, 138, 171, 173, + 175, 146, 35, 53, 59, 68, 138, 170, 172, 173, + 174, 184, 112, 60, 97, 169, 146, 60, 93, 167, + 65, 75, 146, 8, 147, 60, 72, 72, 60, 94, + 65, 146, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, + 126, 126, 126, 130, 60, 135, 187, 59, 141, 126, + 192, 182, 126, 130, 1, 67, 91, 100, 180, 181, + 183, 186, 186, 126, 8, 17, 22, 23, 24, 29, + 36, 53, 65, 71, 142, 202, 204, 205, 206, 141, + 207, 215, 162, 59, 3, 202, 202, 83, 60, 179, + 8, 146, 60, 141, 35, 105, 5, 65, 62, 146, + 136, 145, 75, 191, 60, 179, 183, 115, 62, 63, + 24, 173, 59, 176, 62, 190, 72, 104, 59, 174, + 53, 174, 62, 190, 3, 198, 75, 146, 123, 62, + 190, 62, 190, 186, 139, 65, 36, 59, 146, 152, + 153, 154, 161, 67, 146, 146, 62, 190, 186, 65, + 67, 126, 131, 132, 188, 189, 11, 75, 191, 31, + 135, 72, 66, 180, 75, 191, 189, 101, 62, 68, + 36, 59, 203, 204, 206, 59, 67, 71, 67, 8, + 202, 3, 50, 59, 141, 212, 213, 3, 72, 65, + 11, 202, 60, 75, 62, 195, 215, 62, 62, 62, + 60, 60, 106, 26, 26, 194, 177, 59, 141, 151, + 152, 153, 154, 155, 161, 163, 60, 68, 105, 194, + 141, 60, 179, 175, 68, 146, 7, 12, 68, 99, + 102, 174, 198, 174, 60, 172, 68, 138, 198, 35, + 97, 60, 93, 60, 186, 146, 130, 94, 95, 168, + 185, 60, 186, 130, 66, 75, 191, 68, 191, 135, + 60, 60, 60, 192, 60, 68, 183, 180, 202, 205, + 195, 24, 141, 142, 197, 202, 209, 217, 202, 141, + 196, 208, 216, 202, 3, 212, 62, 72, 202, 213, + 202, 198, 141, 207, 60, 183, 126, 126, 62, 179, + 59, 163, 116, 60, 187, 66, 103, 60, 60, 198, + 104, 60, 189, 62, 190, 146, 189, 67, 126, 133, + 131, 132, 60, 72, 68, 60, 60, 59, 68, 62, + 72, 202, 68, 62, 49, 202, 62, 198, 59, 59, + 202, 210, 211, 68, 194, 60, 179, 119, 163, 5, + 65, 66, 75, 183, 198, 198, 68, 68, 95, 60, + 68, 130, 210, 195, 209, 202, 198, 208, 212, 195, + 195, 60, 14, 117, 120, 126, 126, 189, 60, 60, + 60, 60, 163, 20, 100, 66, 66, 68, 210, 210, + 118, 112, 105 }; #define yyerrok (yyerrstatus = 0) @@ -1567,18 +1565,9 @@ static const yytype_uint8 yystos[] = /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. - Once GCC version 2 has supplanted version 1, this can go. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ + Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) @@ -1588,6 +1577,7 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -1629,10 +1619,19 @@ while (YYID (0)) #endif -/* This macro is provided for backward compatibility. */ +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif #endif @@ -1736,20 +1735,17 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } @@ -1783,11 +1779,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF (stderr, " $%d = ", yyi + 1); + fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - YYFPRINTF (stderr, "\n"); + fprintf (stderr, "\n"); } } @@ -1824,6 +1820,7 @@ int yydebug; # define YYMAXDEPTH 10000 #endif + #if YYERROR_VERBOSE @@ -1926,142 +1923,115 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return 2 if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) { - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ - const char *yyformat = 0; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ - int yycount = 0; - - /* There are many possibilities here to consider: - - Assume YYFAIL is not used. It's too flawed to consider. See - <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> - for details. YYERROR is fine as it does not invoke this - function. - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yytoken != YYEMPTY) - { - int yyn = yypact[*yyssp]; - yyarg[yycount++] = yytname[yytoken]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - } - } + int yyn = yypact[yystate]; - switch (yycount) - { -# define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -# undef YYCASE_ - } + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } - yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return 1; - } + if (yysize_overflow) + return YYSIZE_MAXIMUM; - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyformat += 2; - } - else - { - yyp++; - yyformat++; - } - } - return 0; + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } } #endif /* YYERROR_VERBOSE */ + /*-----------------------------------------------. | Release the memory associated to this symbol. | @@ -2093,9 +2063,10 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - + /* Prevent warnings from -Wmissing-prototypes. */ + #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -2111,16 +2082,18 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ -/* The lookahead symbol. */ + +/* The look-ahead symbol. */ int yychar, yystate; -/* The semantic value of the lookahead symbol. */ +/* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; + /*----------. | yyparse. | `----------*/ @@ -2147,36 +2120,13 @@ yyparse () #endif #endif { - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; - - YYSIZE_T yystacksize; - + int yyn; int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; @@ -2184,28 +2134,51 @@ yyparse () YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; - yystacksize = YYINITDEPTH; - YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ + yyssp = yyss; yyvsp = yyvs; @@ -2235,6 +2208,7 @@ yyparse () YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; + /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might @@ -2242,6 +2216,7 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); yyss = yyss1; @@ -2264,8 +2239,9 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -2276,6 +2252,7 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; + YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -2285,9 +2262,6 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - if (yystate == YYFINAL) - YYACCEPT; - goto yybackup; /*-----------. @@ -2296,16 +2270,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ + look-ahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to lookahead token. */ + /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) + if (yyn == YYPACT_NINF) goto yydefault; - /* Not known => get a lookahead token if don't already have one. */ + /* Not known => get a look-ahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -2331,22 +2305,26 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yytable_value_is_error (yyn)) - goto yyerrlab; + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; yyn = -yyn; goto yyreduce; } + if (yyn == YYFINAL) + YYACCEPT; + /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; - /* Shift the lookahead token. */ + /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token. */ - yychar = YYEMPTY; + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -2386,8 +2364,6 @@ yyreduce: switch (yyn) { case 2: - -/* Line 1806 of yacc.c */ #line 128 "go.y" { xtop = concat(xtop, (yyvsp[(4) - (4)].list)); @@ -2395,8 +2371,6 @@ yyreduce: break; case 3: - -/* Line 1806 of yacc.c */ #line 134 "go.y" { prevlineno = lineno; @@ -2407,8 +2381,6 @@ yyreduce: break; case 4: - -/* Line 1806 of yacc.c */ #line 141 "go.y" { mkpackage((yyvsp[(2) - (3)].sym)->name); @@ -2416,8 +2388,6 @@ yyreduce: break; case 5: - -/* Line 1806 of yacc.c */ #line 151 "go.y" { importpkg = runtimepkg; @@ -2431,8 +2401,6 @@ yyreduce: break; case 6: - -/* Line 1806 of yacc.c */ #line 162 "go.y" { importpkg = nil; @@ -2440,8 +2408,6 @@ yyreduce: break; case 12: - -/* Line 1806 of yacc.c */ #line 176 "go.y" { Pkg *ipkg; @@ -2478,8 +2444,6 @@ yyreduce: break; case 13: - -/* Line 1806 of yacc.c */ #line 209 "go.y" { // When an invalid import path is passed to importfile, @@ -2492,8 +2456,6 @@ yyreduce: break; case 16: - -/* Line 1806 of yacc.c */ #line 224 "go.y" { // import with original name @@ -2504,8 +2466,6 @@ yyreduce: break; case 17: - -/* Line 1806 of yacc.c */ #line 231 "go.y" { // import with given name @@ -2516,8 +2476,6 @@ yyreduce: break; case 18: - -/* Line 1806 of yacc.c */ #line 238 "go.y" { // import into my name space @@ -2528,8 +2486,6 @@ yyreduce: break; case 19: - -/* Line 1806 of yacc.c */ #line 247 "go.y" { if(importpkg->name == nil) { @@ -2545,8 +2501,6 @@ yyreduce: break; case 21: - -/* Line 1806 of yacc.c */ #line 261 "go.y" { if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0) @@ -2555,8 +2509,6 @@ yyreduce: break; case 22: - -/* Line 1806 of yacc.c */ #line 267 "go.y" { defercheckwidth(); @@ -2564,8 +2516,6 @@ yyreduce: break; case 23: - -/* Line 1806 of yacc.c */ #line 271 "go.y" { resumecheckwidth(); @@ -2574,8 +2524,6 @@ yyreduce: break; case 24: - -/* Line 1806 of yacc.c */ #line 280 "go.y" { yyerror("empty top-level declaration"); @@ -2584,8 +2532,6 @@ yyreduce: break; case 26: - -/* Line 1806 of yacc.c */ #line 286 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); @@ -2593,8 +2539,6 @@ yyreduce: break; case 27: - -/* Line 1806 of yacc.c */ #line 290 "go.y" { yyerror("non-declaration statement outside function body"); @@ -2603,8 +2547,6 @@ yyreduce: break; case 28: - -/* Line 1806 of yacc.c */ #line 295 "go.y" { (yyval.list) = nil; @@ -2612,8 +2554,6 @@ yyreduce: break; case 29: - -/* Line 1806 of yacc.c */ #line 301 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2621,8 +2561,6 @@ yyreduce: break; case 30: - -/* Line 1806 of yacc.c */ #line 305 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2630,8 +2568,6 @@ yyreduce: break; case 31: - -/* Line 1806 of yacc.c */ #line 309 "go.y" { (yyval.list) = nil; @@ -2639,8 +2575,6 @@ yyreduce: break; case 32: - -/* Line 1806 of yacc.c */ #line 313 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2650,8 +2584,6 @@ yyreduce: break; case 33: - -/* Line 1806 of yacc.c */ #line 319 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2661,8 +2593,6 @@ yyreduce: break; case 34: - -/* Line 1806 of yacc.c */ #line 325 "go.y" { (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list)); @@ -2672,8 +2602,6 @@ yyreduce: break; case 35: - -/* Line 1806 of yacc.c */ #line 331 "go.y" { (yyval.list) = nil; @@ -2682,8 +2610,6 @@ yyreduce: break; case 36: - -/* Line 1806 of yacc.c */ #line 336 "go.y" { (yyval.list) = list1((yyvsp[(2) - (2)].node)); @@ -2691,8 +2617,6 @@ yyreduce: break; case 37: - -/* Line 1806 of yacc.c */ #line 340 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2700,8 +2624,6 @@ yyreduce: break; case 38: - -/* Line 1806 of yacc.c */ #line 344 "go.y" { (yyval.list) = nil; @@ -2709,8 +2631,6 @@ yyreduce: break; case 39: - -/* Line 1806 of yacc.c */ #line 350 "go.y" { iota = 0; @@ -2718,8 +2638,6 @@ yyreduce: break; case 40: - -/* Line 1806 of yacc.c */ #line 356 "go.y" { (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2727,8 +2645,6 @@ yyreduce: break; case 41: - -/* Line 1806 of yacc.c */ #line 360 "go.y" { (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2736,8 +2652,6 @@ yyreduce: break; case 42: - -/* Line 1806 of yacc.c */ #line 364 "go.y" { (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list)); @@ -2745,8 +2659,6 @@ yyreduce: break; case 43: - -/* Line 1806 of yacc.c */ #line 370 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2754,8 +2666,6 @@ yyreduce: break; case 44: - -/* Line 1806 of yacc.c */ #line 374 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list)); @@ -2763,8 +2673,6 @@ yyreduce: break; case 46: - -/* Line 1806 of yacc.c */ #line 381 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2772,8 +2680,6 @@ yyreduce: break; case 47: - -/* Line 1806 of yacc.c */ #line 385 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil); @@ -2781,8 +2687,6 @@ yyreduce: break; case 48: - -/* Line 1806 of yacc.c */ #line 391 "go.y" { // different from dclname because the name @@ -2793,8 +2697,6 @@ yyreduce: break; case 49: - -/* Line 1806 of yacc.c */ #line 400 "go.y" { (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1); @@ -2802,18 +2704,28 @@ yyreduce: break; case 50: - -/* Line 1806 of yacc.c */ #line 406 "go.y" { (yyval.node) = (yyvsp[(1) - (1)].node); + + // These nodes do not carry line numbers. + // Since a bare name used as an expression is an error, + // introduce a wrapper node to give the correct line. + switch((yyval.node)->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + (yyval.node) = nod(OPAREN, (yyval.node), N); + (yyval.node)->implicit = 1; + break; + } } break; case 51: - -/* Line 1806 of yacc.c */ -#line 410 "go.y" +#line 424 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); (yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode @@ -2821,9 +2733,7 @@ yyreduce: break; case 52: - -/* Line 1806 of yacc.c */ -#line 415 "go.y" +#line 429 "go.y" { if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) { // simple @@ -2838,9 +2748,7 @@ yyreduce: break; case 53: - -/* Line 1806 of yacc.c */ -#line 427 "go.y" +#line 441 "go.y" { if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) { (yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right); @@ -2854,14 +2762,12 @@ yyreduce: (yyval.node)->left = dclname((yyvsp[(1) - (3)].list)->n->sym); // it's a colas, so must not re-use an oldname. break; } - (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); + (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list), (yyvsp[(2) - (3)].i)); } break; case 54: - -/* Line 1806 of yacc.c */ -#line 443 "go.y" +#line 457 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OADD; @@ -2869,9 +2775,7 @@ yyreduce: break; case 55: - -/* Line 1806 of yacc.c */ -#line 448 "go.y" +#line 462 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); (yyval.node)->etype = OSUB; @@ -2879,9 +2783,7 @@ yyreduce: break; case 56: - -/* Line 1806 of yacc.c */ -#line 455 "go.y" +#line 469 "go.y" { Node *n, *nn; @@ -2904,9 +2806,7 @@ yyreduce: break; case 57: - -/* Line 1806 of yacc.c */ -#line 475 "go.y" +#line 489 "go.y" { Node *n; @@ -2927,23 +2827,19 @@ yyreduce: break; case 58: - -/* Line 1806 of yacc.c */ -#line 493 "go.y" +#line 507 "go.y" { // will be converted to OCASE // right will point to next case // done in casebody() markdcl(); (yyval.node) = nod(OXCASE, N, N); - (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)))); + (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)), (yyvsp[(3) - (5)].i))); } break; case 59: - -/* Line 1806 of yacc.c */ -#line 502 "go.y" +#line 516 "go.y" { Node *n, *nn; @@ -2962,18 +2858,14 @@ yyreduce: break; case 60: - -/* Line 1806 of yacc.c */ -#line 520 "go.y" +#line 534 "go.y" { markdcl(); } break; case 61: - -/* Line 1806 of yacc.c */ -#line 524 "go.y" +#line 538 "go.y" { (yyval.node) = liststmt((yyvsp[(3) - (4)].list)); popdcl(); @@ -2981,9 +2873,7 @@ yyreduce: break; case 62: - -/* Line 1806 of yacc.c */ -#line 531 "go.y" +#line 545 "go.y" { // If the last token read by the lexer was consumed // as part of the case, clear it (parser has cleared yychar). @@ -2996,9 +2886,7 @@ yyreduce: break; case 63: - -/* Line 1806 of yacc.c */ -#line 541 "go.y" +#line 555 "go.y" { int last; @@ -3020,36 +2908,28 @@ yyreduce: break; case 64: - -/* Line 1806 of yacc.c */ -#line 561 "go.y" +#line 575 "go.y" { (yyval.list) = nil; } break; case 65: - -/* Line 1806 of yacc.c */ -#line 565 "go.y" +#line 579 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: - -/* Line 1806 of yacc.c */ -#line 571 "go.y" +#line 585 "go.y" { markdcl(); } break; case 67: - -/* Line 1806 of yacc.c */ -#line 575 "go.y" +#line 589 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -3057,9 +2937,7 @@ yyreduce: break; case 68: - -/* Line 1806 of yacc.c */ -#line 582 "go.y" +#line 596 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3068,9 +2946,7 @@ yyreduce: break; case 69: - -/* Line 1806 of yacc.c */ -#line 588 "go.y" +#line 602 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3080,9 +2956,7 @@ yyreduce: break; case 70: - -/* Line 1806 of yacc.c */ -#line 597 "go.y" +#line 611 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -3096,9 +2970,7 @@ yyreduce: break; case 71: - -/* Line 1806 of yacc.c */ -#line 608 "go.y" +#line 622 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -3107,9 +2979,7 @@ yyreduce: break; case 73: - -/* Line 1806 of yacc.c */ -#line 617 "go.y" +#line 631 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -3117,18 +2987,14 @@ yyreduce: break; case 74: - -/* Line 1806 of yacc.c */ -#line 624 "go.y" +#line 638 "go.y" { markdcl(); } break; case 75: - -/* Line 1806 of yacc.c */ -#line 628 "go.y" +#line 642 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3136,9 +3002,7 @@ yyreduce: break; case 76: - -/* Line 1806 of yacc.c */ -#line 635 "go.y" +#line 649 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3147,9 +3011,7 @@ yyreduce: break; case 77: - -/* Line 1806 of yacc.c */ -#line 641 "go.y" +#line 655 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3160,18 +3022,14 @@ yyreduce: break; case 78: - -/* Line 1806 of yacc.c */ -#line 652 "go.y" +#line 666 "go.y" { markdcl(); } break; case 79: - -/* Line 1806 of yacc.c */ -#line 656 "go.y" +#line 670 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3179,66 +3037,89 @@ yyreduce: break; case 80: - -/* Line 1806 of yacc.c */ -#line 661 "go.y" +#line 675 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 81: - -/* Line 1806 of yacc.c */ -#line 665 "go.y" +#line 679 "go.y" { + Node *n; + NodeList *nn; + + (yyval.node) = (yyvsp[(3) - (8)].node); + n = (yyvsp[(3) - (8)].node); popdcl(); - (yyval.node) = (yyvsp[(3) - (7)].node); - if((yyvsp[(7) - (7)].node) != N) - (yyval.node)->nelse = list1((yyvsp[(7) - (7)].node)); + for(nn = concat((yyvsp[(7) - (8)].list), (yyvsp[(8) - (8)].list)); nn; nn = nn->next) { + if(nn->n->op == OIF) + popdcl(); + n->nelse = list1(nn->n); + n = nn->n; + } } break; case 82: - -/* Line 1806 of yacc.c */ -#line 673 "go.y" +#line 696 "go.y" { - (yyval.node) = N; + markdcl(); } break; case 83: - -/* Line 1806 of yacc.c */ -#line 677 "go.y" +#line 700 "go.y" { - (yyval.node) = (yyvsp[(2) - (2)].node); + if((yyvsp[(4) - (5)].node)->ntest == N) + yyerror("missing condition in if statement"); + (yyvsp[(4) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); + (yyval.list) = list1((yyvsp[(4) - (5)].node)); } break; case 84: - -/* Line 1806 of yacc.c */ -#line 681 "go.y" +#line 708 "go.y" { - (yyval.node) = (yyvsp[(2) - (2)].node); + (yyval.list) = nil; } break; case 85: - -/* Line 1806 of yacc.c */ -#line 687 "go.y" +#line 712 "go.y" { - markdcl(); + (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 86: +#line 717 "go.y" + { + (yyval.list) = nil; + } + break; + + case 87: +#line 721 "go.y" + { + NodeList *node; + + node = mal(sizeof *node); + node->n = (yyvsp[(2) - (2)].node); + node->end = node; + (yyval.list) = node; + } + break; + + case 88: +#line 732 "go.y" + { + markdcl(); + } + break; -/* Line 1806 of yacc.c */ -#line 691 "go.y" + case 89: +#line 736 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3248,10 +3129,8 @@ yyreduce: } break; - case 87: - -/* Line 1806 of yacc.c */ -#line 699 "go.y" + case 90: +#line 744 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3261,19 +3140,15 @@ yyreduce: } break; - case 88: - -/* Line 1806 of yacc.c */ -#line 709 "go.y" + case 91: +#line 754 "go.y" { typesw = nod(OXXX, typesw, N); } break; - case 89: - -/* Line 1806 of yacc.c */ -#line 713 "go.y" + case 92: +#line 758 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3282,199 +3157,155 @@ yyreduce: } break; - case 91: - -/* Line 1806 of yacc.c */ -#line 726 "go.y" + case 94: +#line 771 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 92: - -/* Line 1806 of yacc.c */ -#line 730 "go.y" + case 95: +#line 775 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 93: - -/* Line 1806 of yacc.c */ -#line 734 "go.y" + case 96: +#line 779 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 94: - -/* Line 1806 of yacc.c */ -#line 738 "go.y" + case 97: +#line 783 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 95: - -/* Line 1806 of yacc.c */ -#line 742 "go.y" + case 98: +#line 787 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 96: - -/* Line 1806 of yacc.c */ -#line 746 "go.y" + case 99: +#line 791 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 97: - -/* Line 1806 of yacc.c */ -#line 750 "go.y" + case 100: +#line 795 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 98: - -/* Line 1806 of yacc.c */ -#line 754 "go.y" + case 101: +#line 799 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 99: - -/* Line 1806 of yacc.c */ -#line 758 "go.y" + case 102: +#line 803 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 100: - -/* Line 1806 of yacc.c */ -#line 762 "go.y" + case 103: +#line 807 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 101: - -/* Line 1806 of yacc.c */ -#line 766 "go.y" + case 104: +#line 811 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 102: - -/* Line 1806 of yacc.c */ -#line 770 "go.y" + case 105: +#line 815 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 103: - -/* Line 1806 of yacc.c */ -#line 774 "go.y" + case 106: +#line 819 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 104: - -/* Line 1806 of yacc.c */ -#line 778 "go.y" + case 107: +#line 823 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 105: - -/* Line 1806 of yacc.c */ -#line 782 "go.y" + case 108: +#line 827 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 106: - -/* Line 1806 of yacc.c */ -#line 786 "go.y" + case 109: +#line 831 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 107: - -/* Line 1806 of yacc.c */ -#line 790 "go.y" + case 110: +#line 835 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 108: - -/* Line 1806 of yacc.c */ -#line 794 "go.y" + case 111: +#line 839 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 109: - -/* Line 1806 of yacc.c */ -#line 798 "go.y" + case 112: +#line 843 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 110: - -/* Line 1806 of yacc.c */ -#line 803 "go.y" + case 113: +#line 848 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 112: - -/* Line 1806 of yacc.c */ -#line 810 "go.y" + case 115: +#line 855 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 113: - -/* Line 1806 of yacc.c */ -#line 814 "go.y" + case 116: +#line 859 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3487,84 +3318,66 @@ yyreduce: } break; - case 114: - -/* Line 1806 of yacc.c */ -#line 825 "go.y" + case 117: +#line 870 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; - case 115: - -/* Line 1806 of yacc.c */ -#line 829 "go.y" + case 118: +#line 874 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; - case 116: - -/* Line 1806 of yacc.c */ -#line 833 "go.y" + case 119: +#line 878 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; - case 117: - -/* Line 1806 of yacc.c */ -#line 837 "go.y" + case 120: +#line 882 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; - case 118: - -/* Line 1806 of yacc.c */ -#line 842 "go.y" + case 121: +#line 887 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; - case 119: - -/* Line 1806 of yacc.c */ -#line 846 "go.y" + case 122: +#line 891 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; - case 120: - -/* Line 1806 of yacc.c */ -#line 856 "go.y" + case 123: +#line 901 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; - case 121: - -/* Line 1806 of yacc.c */ -#line 860 "go.y" + case 124: +#line 905 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); } break; - case 122: - -/* Line 1806 of yacc.c */ -#line 865 "go.y" + case 125: +#line 910 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3572,19 +3385,15 @@ yyreduce: } break; - case 123: - -/* Line 1806 of yacc.c */ -#line 873 "go.y" + case 126: +#line 918 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; - case 125: - -/* Line 1806 of yacc.c */ -#line 878 "go.y" + case 128: +#line 923 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3597,57 +3406,45 @@ yyreduce: } break; - case 126: - -/* Line 1806 of yacc.c */ -#line 889 "go.y" + case 129: +#line 934 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; - case 127: - -/* Line 1806 of yacc.c */ -#line 893 "go.y" + case 130: +#line 938 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; - case 128: - -/* Line 1806 of yacc.c */ -#line 897 "go.y" + case 131: +#line 942 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; - case 129: - -/* Line 1806 of yacc.c */ -#line 901 "go.y" + case 132: +#line 946 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; - case 131: - -/* Line 1806 of yacc.c */ -#line 906 "go.y" + case 134: +#line 951 "go.y" { // conversion - (yyval.node) = nod(OCALL, (yyvsp[(1) - (4)].node), N); - (yyval.node)->list = list1((yyvsp[(3) - (4)].node)); + (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); + (yyval.node)->list = list1((yyvsp[(3) - (5)].node)); } break; - case 132: - -/* Line 1806 of yacc.c */ -#line 912 "go.y" + case 135: +#line 957 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3656,10 +3453,8 @@ yyreduce: } break; - case 133: - -/* Line 1806 of yacc.c */ -#line 919 "go.y" + case 136: +#line 964 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3667,10 +3462,8 @@ yyreduce: } break; - case 134: - -/* Line 1806 of yacc.c */ -#line 925 "go.y" + case 137: +#line 970 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3679,10 +3472,8 @@ yyreduce: } break; - case 136: - -/* Line 1806 of yacc.c */ -#line 934 "go.y" + case 139: +#line 979 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3690,29 +3481,51 @@ yyreduce: } break; - case 137: - -/* Line 1806 of yacc.c */ -#line 942 "go.y" + case 140: +#line 987 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; - case 139: + case 141: +#line 993 "go.y" + { + // These nodes do not carry line numbers. + // Since a composite literal commonly spans several lines, + // the line number on errors may be misleading. + // Introduce a wrapper node to give the correct line. + (yyval.node) = (yyvsp[(1) - (1)].node); + switch((yyval.node)->op) { + case ONAME: + case ONONAME: + case OTYPE: + case OPACK: + case OLITERAL: + (yyval.node) = nod(OPAREN, (yyval.node), N); + (yyval.node)->implicit = 1; + } + } + break; -/* Line 1806 of yacc.c */ -#line 949 "go.y" + case 142: +#line 1010 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); } break; - case 141: + case 144: +#line 1018 "go.y" + { + (yyval.node) = (yyvsp[(2) - (4)].node); + (yyval.node)->list = (yyvsp[(3) - (4)].list); + } + break; -/* Line 1806 of yacc.c */ -#line 957 "go.y" + case 146: +#line 1026 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3725,33 +3538,28 @@ yyreduce: case OPACK: case OTYPE: case OLITERAL: + case OTYPESW: (yyval.node) = nod(OPAREN, (yyval.node), N); } } break; - case 145: - -/* Line 1806 of yacc.c */ -#line 982 "go.y" + case 150: +#line 1052 "go.y" { (yyval.i) = LBODY; } break; - case 146: - -/* Line 1806 of yacc.c */ -#line 986 "go.y" + case 151: +#line 1056 "go.y" { (yyval.i) = '{'; } break; - case 147: - -/* Line 1806 of yacc.c */ -#line 997 "go.y" + case 152: +#line 1067 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3760,28 +3568,22 @@ yyreduce: } break; - case 148: - -/* Line 1806 of yacc.c */ -#line 1006 "go.y" + case 153: +#line 1076 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; - case 149: - -/* Line 1806 of yacc.c */ -#line 1011 "go.y" + case 154: +#line 1081 "go.y" { (yyval.node) = N; } break; - case 151: - -/* Line 1806 of yacc.c */ -#line 1018 "go.y" + case 156: +#line 1088 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3790,31 +3592,31 @@ yyreduce: } break; - case 153: - -/* Line 1806 of yacc.c */ -#line 1026 "go.y" + case 158: +#line 1096 "go.y" { (yyval.sym) = S; } break; - case 154: - -/* Line 1806 of yacc.c */ -#line 1032 "go.y" + case 159: +#line 1102 "go.y" { + Pkg *p; + if((yyvsp[(2) - (4)].val).u.sval->len == 0) - (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, importpkg); - else - (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, mkpkg((yyvsp[(2) - (4)].val).u.sval)); + p = importpkg; + else { + if(isbadimport((yyvsp[(2) - (4)].val).u.sval)) + errorexit(); + p = mkpkg((yyvsp[(2) - (4)].val).u.sval); + } + (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, p); } break; - case 155: - -/* Line 1806 of yacc.c */ -#line 1041 "go.y" + case 160: +#line 1117 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3822,56 +3624,44 @@ yyreduce: } break; - case 157: - -/* Line 1806 of yacc.c */ -#line 1061 "go.y" + case 162: +#line 1137 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); } break; - case 158: - -/* Line 1806 of yacc.c */ -#line 1066 "go.y" + case 163: +#line 1142 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; - case 164: - -/* Line 1806 of yacc.c */ -#line 1077 "go.y" + case 169: +#line 1153 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 168: - -/* Line 1806 of yacc.c */ -#line 1086 "go.y" + case 173: +#line 1162 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 173: - -/* Line 1806 of yacc.c */ -#line 1096 "go.y" + case 178: +#line 1172 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; - case 183: - -/* Line 1806 of yacc.c */ -#line 1117 "go.y" + case 188: +#line 1193 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3884,77 +3674,61 @@ yyreduce: } break; - case 184: - -/* Line 1806 of yacc.c */ -#line 1130 "go.y" + case 189: +#line 1206 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; - case 185: - -/* Line 1806 of yacc.c */ -#line 1134 "go.y" + case 190: +#line 1210 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); } break; - case 186: - -/* Line 1806 of yacc.c */ -#line 1139 "go.y" + case 191: +#line 1215 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; } break; - case 187: - -/* Line 1806 of yacc.c */ -#line 1144 "go.y" + case 192: +#line 1220 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; } break; - case 188: - -/* Line 1806 of yacc.c */ -#line 1149 "go.y" + case 193: +#line 1225 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; - case 191: - -/* Line 1806 of yacc.c */ -#line 1157 "go.y" + case 196: +#line 1233 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; - case 192: - -/* Line 1806 of yacc.c */ -#line 1163 "go.y" + case 197: +#line 1239 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; } break; - case 193: - -/* Line 1806 of yacc.c */ -#line 1170 "go.y" + case 198: +#line 1246 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3962,20 +3736,16 @@ yyreduce: } break; - case 194: - -/* Line 1806 of yacc.c */ -#line 1176 "go.y" + case 199: +#line 1252 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 195: - -/* Line 1806 of yacc.c */ -#line 1183 "go.y" + case 200: +#line 1259 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3983,34 +3753,31 @@ yyreduce: } break; - case 196: - -/* Line 1806 of yacc.c */ -#line 1189 "go.y" + case 201: +#line 1265 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); } break; - case 197: - -/* Line 1806 of yacc.c */ -#line 1200 "go.y" + case 202: +#line 1276 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) break; + if(noescape && (yyvsp[(3) - (3)].list) != nil) + yyerror("can only use //go:noescape with external func implementations"); (yyval.node)->nbody = (yyvsp[(3) - (3)].list); (yyval.node)->endlineno = lineno; + (yyval.node)->noescape = noescape; funcbody((yyval.node)); } break; - case 198: - -/* Line 1806 of yacc.c */ -#line 1211 "go.y" + case 203: +#line 1290 "go.y" { Node *t; @@ -4041,10 +3808,8 @@ yyreduce: } break; - case 199: - -/* Line 1806 of yacc.c */ -#line 1240 "go.y" + case 204: +#line 1319 "go.y" { Node *rcvr, *t; @@ -4077,16 +3842,15 @@ yyreduce: (yyval.node)->nname = methodname1((yyval.node)->shortname, rcvr->right); (yyval.node)->nname->defn = (yyval.node); (yyval.node)->nname->ntype = t; + (yyval.node)->nname->nointerface = nointerface; declare((yyval.node)->nname, PFUNC); funchdr((yyval.node)); } break; - case 200: - -/* Line 1806 of yacc.c */ -#line 1279 "go.y" + case 205: +#line 1359 "go.y" { Sym *s; Type *t; @@ -4098,8 +3862,10 @@ yyreduce: importsym(s, ONAME); if(s->def != N && s->def->op == ONAME) { - if(eqtype(t, s->def->type)) + if(eqtype(t, s->def->type)) { + dclcontext = PDISCARD; // since we skip funchdr below break; + } yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t); } @@ -4111,16 +3877,15 @@ yyreduce: } break; - case 201: - -/* Line 1806 of yacc.c */ -#line 1302 "go.y" + case 206: +#line 1384 "go.y" { (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list)); checkwidth((yyval.node)->type); - addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0); + addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0, nointerface); + nointerface = 0; funchdr((yyval.node)); // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as @@ -4131,10 +3896,8 @@ yyreduce: } break; - case 202: - -/* Line 1806 of yacc.c */ -#line 1319 "go.y" + case 207: +#line 1402 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -4143,19 +3906,15 @@ yyreduce: } break; - case 203: - -/* Line 1806 of yacc.c */ -#line 1327 "go.y" + case 208: +#line 1410 "go.y" { (yyval.list) = nil; } break; - case 204: - -/* Line 1806 of yacc.c */ -#line 1331 "go.y" + case 209: +#line 1414 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -4163,149 +3922,119 @@ yyreduce: } break; - case 205: - -/* Line 1806 of yacc.c */ -#line 1339 "go.y" + case 210: +#line 1422 "go.y" { (yyval.list) = nil; } break; - case 206: - -/* Line 1806 of yacc.c */ -#line 1343 "go.y" + case 211: +#line 1426 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; - case 207: - -/* Line 1806 of yacc.c */ -#line 1347 "go.y" + case 212: +#line 1430 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 208: - -/* Line 1806 of yacc.c */ -#line 1354 "go.y" + case 213: +#line 1437 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; - case 209: - -/* Line 1806 of yacc.c */ -#line 1360 "go.y" + case 214: +#line 1443 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); } break; - case 210: - -/* Line 1806 of yacc.c */ -#line 1365 "go.y" + case 215: +#line 1448 "go.y" { (yyval.node) = closurebody(nil); } break; - case 211: - -/* Line 1806 of yacc.c */ -#line 1376 "go.y" + case 216: +#line 1459 "go.y" { (yyval.list) = nil; } break; - case 212: - -/* Line 1806 of yacc.c */ -#line 1380 "go.y" + case 217: +#line 1463 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) testdclstack(); + nointerface = 0; + noescape = 0; } break; - case 214: - -/* Line 1806 of yacc.c */ -#line 1389 "go.y" + case 219: +#line 1474 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 216: - -/* Line 1806 of yacc.c */ -#line 1396 "go.y" + case 221: +#line 1481 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 217: - -/* Line 1806 of yacc.c */ -#line 1402 "go.y" + case 222: +#line 1487 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 218: - -/* Line 1806 of yacc.c */ -#line 1406 "go.y" + case 223: +#line 1491 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 220: - -/* Line 1806 of yacc.c */ -#line 1413 "go.y" + case 225: +#line 1498 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; - case 221: - -/* Line 1806 of yacc.c */ -#line 1419 "go.y" + case 226: +#line 1504 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 222: - -/* Line 1806 of yacc.c */ -#line 1423 "go.y" + case 227: +#line 1508 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 223: - -/* Line 1806 of yacc.c */ -#line 1429 "go.y" + case 228: +#line 1514 "go.y" { NodeList *l; @@ -4330,20 +4059,16 @@ yyreduce: } break; - case 224: - -/* Line 1806 of yacc.c */ -#line 1452 "go.y" + case 229: +#line 1537 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); } break; - case 225: - -/* Line 1806 of yacc.c */ -#line 1457 "go.y" + case 230: +#line 1542 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4351,10 +4076,8 @@ yyreduce: } break; - case 226: - -/* Line 1806 of yacc.c */ -#line 1463 "go.y" + case 231: +#line 1548 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4362,10 +4085,8 @@ yyreduce: } break; - case 227: - -/* Line 1806 of yacc.c */ -#line 1469 "go.y" + case 232: +#line 1554 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4374,10 +4095,8 @@ yyreduce: } break; - case 228: - -/* Line 1806 of yacc.c */ -#line 1476 "go.y" + case 233: +#line 1561 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4386,10 +4105,8 @@ yyreduce: } break; - case 229: - -/* Line 1806 of yacc.c */ -#line 1485 "go.y" + case 234: +#line 1570 "go.y" { Node *n; @@ -4400,10 +4117,8 @@ yyreduce: } break; - case 230: - -/* Line 1806 of yacc.c */ -#line 1494 "go.y" + case 235: +#line 1579 "go.y" { Pkg *pkg; @@ -4418,48 +4133,38 @@ yyreduce: } break; - case 231: - -/* Line 1806 of yacc.c */ -#line 1509 "go.y" + case 236: +#line 1594 "go.y" { (yyval.node) = embedded((yyvsp[(1) - (1)].sym)); } break; - case 232: - -/* Line 1806 of yacc.c */ -#line 1515 "go.y" + case 237: +#line 1600 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); } break; - case 233: - -/* Line 1806 of yacc.c */ -#line 1520 "go.y" + case 238: +#line 1605 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; - case 234: - -/* Line 1806 of yacc.c */ -#line 1524 "go.y" + case 239: +#line 1609 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); } break; - case 235: - -/* Line 1806 of yacc.c */ -#line 1531 "go.y" + case 240: +#line 1616 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4469,10 +4174,8 @@ yyreduce: } break; - case 237: - -/* Line 1806 of yacc.c */ -#line 1545 "go.y" + case 242: +#line 1630 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4480,10 +4183,8 @@ yyreduce: } break; - case 238: - -/* Line 1806 of yacc.c */ -#line 1551 "go.y" + case 243: +#line 1636 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4491,83 +4192,65 @@ yyreduce: } break; - case 240: - -/* Line 1806 of yacc.c */ -#line 1560 "go.y" + case 245: +#line 1645 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 241: - -/* Line 1806 of yacc.c */ -#line 1564 "go.y" + case 246: +#line 1649 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 242: - -/* Line 1806 of yacc.c */ -#line 1569 "go.y" + case 247: +#line 1654 "go.y" { (yyval.list) = nil; } break; - case 243: - -/* Line 1806 of yacc.c */ -#line 1573 "go.y" + case 248: +#line 1658 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 244: - -/* Line 1806 of yacc.c */ -#line 1581 "go.y" + case 249: +#line 1666 "go.y" { (yyval.node) = N; } break; - case 246: - -/* Line 1806 of yacc.c */ -#line 1586 "go.y" + case 251: +#line 1671 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; - case 248: - -/* Line 1806 of yacc.c */ -#line 1591 "go.y" + case 253: +#line 1676 "go.y" { (yyval.node) = N; } break; - case 254: - -/* Line 1806 of yacc.c */ -#line 1602 "go.y" + case 259: +#line 1687 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions } break; - case 255: - -/* Line 1806 of yacc.c */ -#line 1607 "go.y" + case 260: +#line 1692 "go.y" { NodeList *l; @@ -4579,66 +4262,52 @@ yyreduce: } break; - case 256: - -/* Line 1806 of yacc.c */ -#line 1617 "go.y" + case 261: +#line 1702 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); } break; - case 257: - -/* Line 1806 of yacc.c */ -#line 1622 "go.y" + case 262: +#line 1707 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; - case 258: - -/* Line 1806 of yacc.c */ -#line 1626 "go.y" + case 263: +#line 1711 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; - case 259: - -/* Line 1806 of yacc.c */ -#line 1630 "go.y" + case 264: +#line 1715 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; - case 260: - -/* Line 1806 of yacc.c */ -#line 1634 "go.y" + case 265: +#line 1719 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; - case 261: - -/* Line 1806 of yacc.c */ -#line 1638 "go.y" + case 266: +#line 1723 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions } break; - case 262: - -/* Line 1806 of yacc.c */ -#line 1643 "go.y" + case 267: +#line 1728 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4657,10 +4326,8 @@ yyreduce: } break; - case 263: - -/* Line 1806 of yacc.c */ -#line 1662 "go.y" + case 268: +#line 1747 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4668,10 +4335,8 @@ yyreduce: } break; - case 264: - -/* Line 1806 of yacc.c */ -#line 1668 "go.y" + case 269: +#line 1753 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4679,247 +4344,195 @@ yyreduce: } break; - case 265: - -/* Line 1806 of yacc.c */ -#line 1676 "go.y" + case 270: +#line 1761 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 266: - -/* Line 1806 of yacc.c */ -#line 1680 "go.y" + case 271: +#line 1765 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 267: - -/* Line 1806 of yacc.c */ -#line 1686 "go.y" + case 272: +#line 1771 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 268: - -/* Line 1806 of yacc.c */ -#line 1690 "go.y" + case 273: +#line 1775 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 269: - -/* Line 1806 of yacc.c */ -#line 1696 "go.y" + case 274: +#line 1781 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 270: - -/* Line 1806 of yacc.c */ -#line 1700 "go.y" + case 275: +#line 1785 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 271: - -/* Line 1806 of yacc.c */ -#line 1706 "go.y" + case 276: +#line 1791 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 272: - -/* Line 1806 of yacc.c */ -#line 1710 "go.y" + case 277: +#line 1795 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 273: - -/* Line 1806 of yacc.c */ -#line 1719 "go.y" + case 278: +#line 1804 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 274: - -/* Line 1806 of yacc.c */ -#line 1723 "go.y" + case 279: +#line 1808 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 275: - -/* Line 1806 of yacc.c */ -#line 1727 "go.y" + case 280: +#line 1812 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 276: - -/* Line 1806 of yacc.c */ -#line 1731 "go.y" + case 281: +#line 1816 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 277: - -/* Line 1806 of yacc.c */ -#line 1736 "go.y" + case 282: +#line 1821 "go.y" { (yyval.list) = nil; } break; - case 278: - -/* Line 1806 of yacc.c */ -#line 1740 "go.y" + case 283: +#line 1825 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; - case 283: - -/* Line 1806 of yacc.c */ -#line 1754 "go.y" + case 288: +#line 1839 "go.y" { (yyval.node) = N; } break; - case 285: - -/* Line 1806 of yacc.c */ -#line 1760 "go.y" + case 290: +#line 1845 "go.y" { (yyval.list) = nil; } break; - case 287: - -/* Line 1806 of yacc.c */ -#line 1766 "go.y" + case 292: +#line 1851 "go.y" { (yyval.node) = N; } break; - case 289: - -/* Line 1806 of yacc.c */ -#line 1772 "go.y" + case 294: +#line 1857 "go.y" { (yyval.list) = nil; } break; - case 291: - -/* Line 1806 of yacc.c */ -#line 1778 "go.y" + case 296: +#line 1863 "go.y" { (yyval.list) = nil; } break; - case 293: - -/* Line 1806 of yacc.c */ -#line 1784 "go.y" + case 298: +#line 1869 "go.y" { (yyval.list) = nil; } break; - case 295: - -/* Line 1806 of yacc.c */ -#line 1790 "go.y" + case 300: +#line 1875 "go.y" { (yyval.val).ctype = CTxxx; } break; - case 297: - -/* Line 1806 of yacc.c */ -#line 1800 "go.y" + case 302: +#line 1885 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; - case 298: - -/* Line 1806 of yacc.c */ -#line 1804 "go.y" + case 303: +#line 1889 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; - case 299: - -/* Line 1806 of yacc.c */ -#line 1808 "go.y" + case 304: +#line 1893 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; - case 300: - -/* Line 1806 of yacc.c */ -#line 1812 "go.y" + case 305: +#line 1897 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; - case 301: - -/* Line 1806 of yacc.c */ -#line 1816 "go.y" + case 306: +#line 1901 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; - case 302: - -/* Line 1806 of yacc.c */ -#line 1820 "go.y" + case 307: +#line 1905 "go.y" { - if((yyvsp[(2) - (4)].node) == N) + if((yyvsp[(2) - (4)].node) == N) { + dclcontext = PEXTERN; // since we skip the funcbody below break; + } (yyvsp[(2) - (4)].node)->inl = (yyvsp[(3) - (4)].list); @@ -4928,45 +4541,37 @@ yyreduce: if(debug['E']) { print("import [%Z] func %lN \n", importpkg->path, (yyvsp[(2) - (4)].node)); - if(debug['l'] > 2 && (yyvsp[(2) - (4)].node)->inl) + if(debug['m'] > 2 && (yyvsp[(2) - (4)].node)->inl) print("inl body:%+H\n", (yyvsp[(2) - (4)].node)->inl); } } break; - case 303: - -/* Line 1806 of yacc.c */ -#line 1838 "go.y" + case 308: +#line 1925 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; } break; - case 304: - -/* Line 1806 of yacc.c */ -#line 1845 "go.y" + case 309: +#line 1932 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); } break; - case 310: - -/* Line 1806 of yacc.c */ -#line 1865 "go.y" + case 315: +#line 1952 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; - case 311: - -/* Line 1806 of yacc.c */ -#line 1869 "go.y" + case 316: +#line 1956 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -4978,64 +4583,50 @@ yyreduce: } break; - case 312: - -/* Line 1806 of yacc.c */ -#line 1879 "go.y" + case 317: +#line 1966 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; - case 313: - -/* Line 1806 of yacc.c */ -#line 1883 "go.y" + case 318: +#line 1970 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; - case 314: - -/* Line 1806 of yacc.c */ -#line 1887 "go.y" + case 319: +#line 1974 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; - case 315: - -/* Line 1806 of yacc.c */ -#line 1891 "go.y" + case 320: +#line 1978 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; - case 316: - -/* Line 1806 of yacc.c */ -#line 1895 "go.y" + case 321: +#line 1982 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; - case 317: - -/* Line 1806 of yacc.c */ -#line 1899 "go.y" + case 322: +#line 1986 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; - case 318: - -/* Line 1806 of yacc.c */ -#line 1903 "go.y" + case 323: +#line 1990 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -5043,10 +4634,8 @@ yyreduce: } break; - case 319: - -/* Line 1806 of yacc.c */ -#line 1909 "go.y" + case 324: +#line 1996 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -5054,10 +4643,8 @@ yyreduce: } break; - case 320: - -/* Line 1806 of yacc.c */ -#line 1915 "go.y" + case 325: +#line 2002 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5065,10 +4652,8 @@ yyreduce: } break; - case 321: - -/* Line 1806 of yacc.c */ -#line 1923 "go.y" + case 326: +#line 2010 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5076,19 +4661,15 @@ yyreduce: } break; - case 322: - -/* Line 1806 of yacc.c */ -#line 1931 "go.y" + case 327: +#line 2018 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; - case 323: - -/* Line 1806 of yacc.c */ -#line 1937 "go.y" + case 328: +#line 2024 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -5097,10 +4678,8 @@ yyreduce: } break; - case 324: - -/* Line 1806 of yacc.c */ -#line 1944 "go.y" + case 329: +#line 2031 "go.y" { Type *t; @@ -5116,10 +4695,8 @@ yyreduce: } break; - case 325: - -/* Line 1806 of yacc.c */ -#line 1960 "go.y" + case 330: +#line 2047 "go.y" { Sym *s; @@ -5137,64 +4714,50 @@ yyreduce: } break; - case 326: - -/* Line 1806 of yacc.c */ -#line 1978 "go.y" + case 331: +#line 2065 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; - case 327: - -/* Line 1806 of yacc.c */ -#line 1982 "go.y" + case 332: +#line 2069 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; - case 328: - -/* Line 1806 of yacc.c */ -#line 1987 "go.y" + case 333: +#line 2074 "go.y" { (yyval.list) = nil; } break; - case 330: - -/* Line 1806 of yacc.c */ -#line 1994 "go.y" + case 335: +#line 2081 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; - case 331: - -/* Line 1806 of yacc.c */ -#line 1998 "go.y" + case 336: +#line 2085 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; - case 332: - -/* Line 1806 of yacc.c */ -#line 2008 "go.y" + case 337: +#line 2095 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; - case 333: - -/* Line 1806 of yacc.c */ -#line 2012 "go.y" + case 338: +#line 2099 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -5211,10 +4774,8 @@ yyreduce: } break; - case 334: - -/* Line 1806 of yacc.c */ -#line 2027 "go.y" + case 339: +#line 2114 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -5222,91 +4783,67 @@ yyreduce: } break; - case 336: - -/* Line 1806 of yacc.c */ -#line 2036 "go.y" + case 341: +#line 2123 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); mpaddfixfix((yyvsp[(2) - (5)].node)->val.u.xval, (yyvsp[(4) - (5)].node)->val.u.xval, 0); break; } + (yyvsp[(4) - (5)].node)->val.u.cval->real = (yyvsp[(4) - (5)].node)->val.u.cval->imag; + mpmovecflt(&(yyvsp[(4) - (5)].node)->val.u.cval->imag, 0.0); (yyval.node) = nodcplxlit((yyvsp[(2) - (5)].node)->val, (yyvsp[(4) - (5)].node)->val); } break; - case 339: - -/* Line 1806 of yacc.c */ -#line 2050 "go.y" + case 344: +#line 2139 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 340: - -/* Line 1806 of yacc.c */ -#line 2054 "go.y" + case 345: +#line 2143 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 341: - -/* Line 1806 of yacc.c */ -#line 2060 "go.y" + case 346: +#line 2149 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 342: - -/* Line 1806 of yacc.c */ -#line 2064 "go.y" + case 347: +#line 2153 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - case 343: - -/* Line 1806 of yacc.c */ -#line 2070 "go.y" + case 348: +#line 2159 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; - case 344: - -/* Line 1806 of yacc.c */ -#line 2074 "go.y" + case 349: +#line 2163 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - -/* Line 1806 of yacc.c */ -#line 5298 "y.tab.c" +/* Line 1267 of yacc.c. */ +#line 4846 "y.tab.c" default: break; } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -5315,6 +4852,7 @@ yyreduce: *++yyvsp = yyval; + /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ @@ -5334,10 +4872,6 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); - /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -5345,36 +4879,37 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else -# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ - yyssp, yytoken) { - char *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = YYSYNTAX_ERROR; - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == 1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); - if (!yymsg) - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = 2; - } - else - { - yysyntax_error_status = YYSYNTAX_ERROR; - yymsgp = yymsg; - } - } - yyerror (yymsgp); - if (yysyntax_error_status == 2) - goto yyexhaustedlab; + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } } -# undef YYSYNTAX_ERROR #endif } @@ -5382,7 +4917,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an + /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -5399,7 +4934,7 @@ yyerrlab: } } - /* Else will try to reuse lookahead token after shifting the error + /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; @@ -5433,7 +4968,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) + if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -5456,6 +4991,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + if (yyn == YYFINAL) + YYACCEPT; + *++yyvsp = yylval; @@ -5480,7 +5018,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -5491,14 +5029,9 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - } + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); @@ -5522,9 +5055,7 @@ yyreturn: } - -/* Line 2067 of yacc.c */ -#line 2078 "go.y" +#line 2167 "go.y" static void diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h index bc6c47d6d..d01fbe198 100644 --- a/src/cmd/gc/y.tab.h +++ b/src/cmd/gc/y.tab.h @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - + the Free Software Foundation; either version 2, or (at your option) + any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work @@ -26,11 +29,10 @@ special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. - + This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ - /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -39,10 +41,10 @@ enum yytokentype { LLITERAL = 258, LASOP = 259, - LBREAK = 260, - LCASE = 261, - LCHAN = 262, - LCOLAS = 263, + LCOLAS = 260, + LBREAK = 261, + LCASE = 262, + LCHAN = 263, LCONST = 264, LCONTINUE = 265, LDDD = 266, @@ -91,10 +93,10 @@ /* Tokens. */ #define LLITERAL 258 #define LASOP 259 -#define LBREAK 260 -#define LCASE 261 -#define LCHAN 262 -#define LCOLAS 263 +#define LCOLAS 260 +#define LBREAK 261 +#define LCASE 262 +#define LCHAN 263 #define LCONST 264 #define LCONTINUE 265 #define LDDD 266 @@ -144,28 +146,22 @@ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - -/* Line 2068 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 2068 of yacc.c */ -#line 163 "y.tab.h" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* Line 1529 of yacc.c. */ +#line 160 "y.tab.h" + YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 #endif extern YYSTYPE yylval; - diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h index 588890d0e..e7eb6516c 100644 --- a/src/cmd/gc/yerr.h +++ b/src/cmd/gc/yerr.h @@ -26,7 +26,7 @@ static struct { 237, ';', "unexpected semicolon or newline before {", - 474, LBODY, + 475, LBODY, "unexpected semicolon or newline before {", 22, '{', @@ -44,7 +44,7 @@ static struct { 37, ',', "unexpected comma in channel type", - 437, LELSE, + 438, LELSE, "unexpected semicolon or newline before else", 257, ',', @@ -65,9 +65,12 @@ static struct { 425, ';', "need trailing comma before newline in composite literal", + 436, ';', + "need trailing comma before newline in composite literal", + 112, LNAME, "nested func not allowed", - 615, ';', + 642, ';', "else must be followed by if or statement block" }; diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index 767ddfd40..38fc43ef1 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -8,6 +8,7 @@ import ( "bytes" "container/heap" "errors" + "flag" "fmt" "go/build" "io" @@ -59,7 +60,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -73,6 +79,9 @@ The build flags are shared by the build, install, run, and test commands: See the documentation for the go/build package for more information about build tags. +The list flags accept a space-separated list of strings. To embed spaces +in an element in the list, surround it with either single or double quotes. + For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, see 'go help gopath'. @@ -99,8 +108,10 @@ var buildX bool // -x flag var buildO = cmdBuild.Flag.String("o", "", "output file") var buildWork bool // -work flag var buildGcflags []string // -gcflags flag +var buildCcflags []string // -ccflags flag var buildLdflags []string // -ldflags flag var buildGccgoflags []string // -gccgoflags flag +var buildRace bool // -race flag var buildContext = build.Default var buildToolchain toolchain = noToolchain{} @@ -115,7 +126,7 @@ func (c buildCompiler) Set(value string) error { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} default: return fmt.Errorf("unknown compiler %q", value) } @@ -132,7 +143,7 @@ func init() { case "gc": buildToolchain = gcToolchain{} case "gccgo": - buildToolchain = gccgcToolchain{} + buildToolchain = gccgoToolchain{} } } @@ -146,10 +157,12 @@ func addBuildFlags(cmd *Command) { cmd.Flag.BoolVar(&buildX, "x", false, "") cmd.Flag.BoolVar(&buildWork, "work", false, "") cmd.Flag.Var((*stringsFlag)(&buildGcflags), "gcflags", "") + cmd.Flag.Var((*stringsFlag)(&buildCcflags), "ccflags", "") cmd.Flag.Var((*stringsFlag)(&buildLdflags), "ldflags", "") cmd.Flag.Var((*stringsFlag)(&buildGccgoflags), "gccgoflags", "") cmd.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") cmd.Flag.Var(buildCompiler{}, "compiler", "") + cmd.Flag.BoolVar(&buildRace, "race", false, "") } func addBuildFlagsNX(cmd *Command) { @@ -157,11 +170,52 @@ func addBuildFlagsNX(cmd *Command) { cmd.Flag.BoolVar(&buildX, "x", false, "") } +func isSpaceByte(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' +} + type stringsFlag []string func (v *stringsFlag) Set(s string) error { - *v = strings.Fields(s) - return nil + var err error + *v, err = splitQuotedFields(s) + return err +} + +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 } func (v *stringsFlag) String() string { @@ -169,6 +223,7 @@ func (v *stringsFlag) String() string { } func runBuild(cmd *Command, args []string) { + raceInit() var b builder b.init() @@ -179,6 +234,21 @@ func runBuild(cmd *Command, args []string) { *buildO += exeSuffix } + // sanity check some often mis-used options + switch buildContext.Compiler { + case "gccgo": + if len(buildGcflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass compiler flags using -gccgoflags, not -gcflags") + } + if len(buildLdflags) != 0 { + fmt.Println("go build: when using gccgo toolchain, please pass linker flags using -gccgoflags, not -ldflags") + } + case "gc": + if len(buildGccgoflags) != 0 { + fmt.Println("go build: when using gc toolchain, please pass compile flags using -gcflags, and linker flags using -ldflags") + } + } + if *buildO != "" { if len(pkgs) > 1 { fatalf("go build: cannot use -o with multiple packages") @@ -213,11 +283,12 @@ See also: go build, go get, go clean. } func runInstall(cmd *Command, args []string) { + raceInit() pkgs := packagesForBuild(args) for _, p := range pkgs { if p.Target == "" && (!p.Standard || p.ImportPath != "unsafe") { - errorf("go install: no install location for %s", p.ImportPath) + errorf("go install: no install location for directory %s outside GOPATH", p.Dir) } } exitIfErrors() @@ -437,7 +508,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action // using cgo, to make sure we do not overwrite the binary while // a package is using it. If this is a cross-build, then the cgo we // are writing is not the cgo we need to use. - if goos == runtime.GOOS && goarch == runtime.GOARCH { + if goos == runtime.GOOS && goarch == runtime.GOARCH && !buildRace { if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" { var stk importStack p1 := loadPackage("cmd/cgo", &stk) @@ -456,7 +527,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action return a } // gccgo standard library is "fake" too. - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // the target name is needed for cgo. a.target = p.target return a @@ -487,9 +558,18 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action a.f = (*builder).build a.target = a.objpkg if a.link { - // An executable file. - // (This is the name of a temporary file.) - a.target = a.objdir + "a.out" + exeSuffix + // An executable file. (This is the name of a temporary file.) + // Because we run the temporary file in 'go run' and 'go test', + // the name will show up in ps listings. If the caller has specified + // a name, use that instead of a.out. The binary is generated + // in an otherwise empty subdirectory named exe to avoid + // naming conflicts. The only possible conflict is if we were + // to create a top-level package named exe. + name := "a.out" + if p.exeName != "" { + name = p.exeName + } + a.target = a.objdir + filepath.Join("exe", name) + exeSuffix } } @@ -535,7 +615,6 @@ func (b *builder) do(root *action) { } b.readySema = make(chan bool, len(all)) - done := make(chan bool) // Initialize per-action execution state. for _, a := range all { @@ -583,10 +662,11 @@ func (b *builder) do(root *action) { if a == root { close(b.readySema) - done <- true } } + var wg sync.WaitGroup + // Kick off goroutines according to parallelism. // If we are using the -n flag (just printing commands) // drop the parallelism to 1, both to make the output @@ -596,19 +676,40 @@ func (b *builder) do(root *action) { par = 1 } for i := 0; i < par; i++ { + wg.Add(1) go func() { - for _ = range b.readySema { - // Receiving a value from b.sema entitles - // us to take from the ready queue. - b.exec.Lock() - a := b.ready.pop() - b.exec.Unlock() - handle(a) + defer wg.Done() + for { + select { + case _, ok := <-b.readySema: + if !ok { + return + } + // Receiving a value from b.readySema entitles + // us to take from the ready queue. + b.exec.Lock() + a := b.ready.pop() + b.exec.Unlock() + handle(a) + case <-interrupted: + setExitStatus(1) + return + } } }() } - <-done + wg.Wait() +} + +// hasString reports whether s appears in the list of strings. +func hasString(strings []string, s string) bool { + for _, t := range strings { + if s == t { + return true + } + } + return false } // build is the action for building a single package or command. @@ -631,12 +732,25 @@ func (b *builder) build(a *action) (err error) { fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath) } + if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && + !hasString(a.p.HFiles, "zasm_"+buildContext.GOOS+"_"+buildContext.GOARCH+".h") { + return fmt.Errorf("%s/%s must be bootstrapped using make.bash", buildContext.GOOS, buildContext.GOARCH) + } + // Make build directory. obj := a.objdir if err := b.mkdir(obj); err != nil { return err } + // make target directory + dir, _ := filepath.Split(a.target) + if dir != "" { + if err := b.mkdir(dir); err != nil { + return err + } + } + var gofiles, cfiles, sfiles, objects, cgoObjects []string gofiles = append(gofiles, a.p.GoFiles...) cfiles = append(cfiles, a.p.CFiles...) @@ -680,16 +794,37 @@ func (b *builder) build(a *action) (err error) { gofiles = append(gofiles, outGo...) } + // Run SWIG. + if a.p.usesSwig() { + // In a package using SWIG, any .c or .s files are + // compiled with gcc. + gccfiles := append(cfiles, sfiles...) + cfiles = nil + sfiles = nil + outGo, outObj, err := b.swig(a.p, obj, gccfiles) + if err != nil { + return err + } + cgoObjects = append(cgoObjects, outObj...) + gofiles = append(gofiles, outGo...) + } + // Prepare Go import path list. inc := b.includeArgs("-I", a.deps) // Compile Go. if len(gofiles) > 0 { - if out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles); err != nil { + ofile, out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles) + if len(out) > 0 { + b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out)) + if err != nil { + return errPrintedOutput + } + } + if err != nil { return err - } else { - objects = append(objects, out) } + objects = append(objects, ofile) } // Copy .h files named for goos or goarch or goos_goarch @@ -718,8 +853,13 @@ func (b *builder) build(a *action) (err error) { } } + objExt := archChar + if _, ok := buildToolchain.(gccgoToolchain); ok { + objExt = "o" + } + for _, file := range cfiles { - out := file[:len(file)-len(".c")] + "." + archChar + out := file[:len(file)-len(".c")] + "." + objExt if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil { return err } @@ -728,7 +868,7 @@ func (b *builder) build(a *action) (err error) { // Assemble .s files. for _, file := range sfiles { - out := file[:len(file)-len(".s")] + "." + archChar + out := file[:len(file)-len(".s")] + "." + objExt if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil { return err } @@ -795,6 +935,20 @@ func (b *builder) install(a *action) (err error) { defer os.Remove(a1.target) } + if a.p.usesSwig() { + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + dir = a.p.swigDir(&buildContext) + if err := b.mkdir(dir); err != nil { + return err + } + soname := a.p.swigSoname(f) + target := filepath.Join(dir, soname) + if err = b.copyFile(a, target, soname, perm); err != nil { + return err + } + } + } + return b.copyFile(a, a.target, a1.target, perm) } @@ -826,10 +980,13 @@ func (b *builder) includeArgs(flag string, all []*action) []string { for _, a1 := range all { if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] { incMap[dir] = true - if _, ok := buildToolchain.(gccgcToolchain); ok { - dir = filepath.Join(dir, "gccgo") + if _, ok := buildToolchain.(gccgoToolchain); ok { + dir = filepath.Join(dir, "gccgo_"+goos+"_"+goarch) } else { dir = filepath.Join(dir, goos+"_"+goarch) + if buildRace { + dir += "_race" + } } inc = append(inc, flag, dir) } @@ -905,6 +1062,8 @@ var objectMagic = [][]byte{ {0xCE, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 32-bit {0xCF, 0xFA, 0xED, 0xFE}, // Mach-O little-endian 64-bit {0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x04, 0x00}, // PE (Windows) as generated by 6l/8l + {0x00, 0x00, 0x01, 0xEB}, // Plan 9 i386 + {0x00, 0x00, 0x8a, 0x97}, // Plan 9 amd64 } func isObject(s string) bool { @@ -1024,19 +1183,18 @@ func relPaths(paths []string) []string { // print this error. var errPrintedOutput = errors.New("already printed output - no need to show error") +var cgoLine = regexp.MustCompile(`\[[^\[\]]+\.cgo1\.go:[0-9]+\]`) + // 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(dir string, desc string, cmdargs ...interface{}) error { out, err := b.runOut(dir, desc, cmdargs...) if len(out) > 0 { - if out[len(out)-1] != '\n' { - out = append(out, '\n') - } if desc == "" { desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " ")) } - b.showOutput(dir, desc, string(out)) + b.showOutput(dir, desc, b.processOutput(out)) if err != nil { err = errPrintedOutput } @@ -1044,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error { return err } +// processOutput prepares the output of runOut to be output to the console. +func (b *builder) processOutput(out []byte) string { + if out[len(out)-1] != '\n' { + out = append(out, '\n') + } + messages := string(out) + // Fix up output referring to cgo-generated code to be more readable. + // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19. + // Replace _Ctype_foo with C.foo. + // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite. + if !buildX && cgoLine.MatchString(messages) { + messages = cgoLine.ReplaceAllString(messages, "") + messages = strings.Replace(messages, "type _Ctype_", "type C.", -1) + } + return messages +} + // runOut runs the command given by cmdline in the directory dir. // It returns the command output and any errors that occurred. func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) { @@ -1062,7 +1237,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt cmd.Stdout = &buf cmd.Stderr = &buf cmd.Dir = dir - // TODO: cmd.Env + cmd.Env = envForDir(cmd.Dir) err := cmd.Run() // cmd.Run will fail on Unix if some other process has the binary @@ -1077,7 +1252,7 @@ func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byt // not worry about other processes inheriting the fd accidentally. // The answer is that running a command is fork and exec. // A child forked while the cgo fd is open inherits that fd. - // Until the child has called exec, it holds the fd open and the + // Until the child has called exec, it holds the fd open and the // kernel will not let us run cgo. Even if the child were to close // the fd explicitly, it would still be open from the time of the fork // until the time of the explicit close, and the race would remain. @@ -1161,7 +1336,7 @@ type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. // The compiler runs in the directory dir. - gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) + gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error @@ -1198,8 +1373,8 @@ func (noToolchain) linker() string { return "" } -func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { - return "", noCompiler() +func (noToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error) { + return "", nil, noCompiler() } func (noToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { @@ -1234,7 +1409,7 @@ func (gcToolchain) linker() string { return tool(archChar + "l") } -func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := "_go_." + archChar ofile = obj + out gcargs := []string{"-p", p.ImportPath} @@ -1244,16 +1419,33 @@ func (gcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, g gcargs = append(gcargs, "-+") } + // If we're giving the compiler the entire package (no C etc files), tell it that, + // so that it can give good error messages about forward declarations. + // Exceptions: a few standard packages have forward declarations for + // pieces supplied behind-the-scenes by package runtime. + extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) + if p.Standard { + switch p.ImportPath { + case "os", "runtime/pprof", "sync", "time": + extFiles++ + } + } + if extFiles == 0 { + gcargs = append(gcargs, "-complete") + } + args := stringList(tool(archChar+"g"), "-o", ofile, buildGcflags, gcargs, "-D", p.localPrefix, importArgs) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + return b.run(p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) } func (gcToolchain) pkgpath(basedir string, p *Package) string { @@ -1266,62 +1458,86 @@ func (gcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []s for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) } - return b.run(p.Dir, p.ImportPath, tool("pack"), "grc", mkAbs(objDir, afile), absOfiles) + return b.run(p.Dir, p.ImportPath, tool("pack"), "grcP", b.work, mkAbs(objDir, afile), absOfiles) } func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { importArgs := b.includeArgs("-L", allactions) - return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, buildLdflags, mainpkg) + swigDirs := make(map[string]bool) + swigArg := []string{} + for _, a := range allactions { + if a.p != nil && a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + if len(swigArg) == 0 { + swigArg = []string{"-r", sd} + } else if !swigDirs[sd] { + swigArg[1] += ":" + swigArg[1] += sd + } + swigDirs[sd] = true + } + } + return b.run(".", p.ImportPath, tool(archChar+"l"), "-o", out, importArgs, swigArg, buildLdflags, mainpkg) } func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) - return b.run(p.Dir, p.ImportPath, tool(archChar+"c"), "-FVw", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, cfile) + args := stringList(tool(archChar+"c"), "-F", "-V", "-w", "-I", objdir, "-I", inc, "-o", ofile, buildCcflags, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, cfile) + return b.run(p.Dir, p.ImportPath, args) } // The Gccgo toolchain. -type gccgcToolchain struct{} +type gccgoToolchain struct{} var gccgoBin, _ = exec.LookPath("gccgo") -func (gccgcToolchain) compiler() string { +func (gccgoToolchain) compiler() string { return gccgoBin } -func (gccgcToolchain) linker() string { +func (gccgoToolchain) linker() string { return gccgoBin } -func (gccgcToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) { +func (gccgoToolchain) gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, output []byte, err error) { out := p.Name + ".o" ofile = obj + out gcargs := []string{"-g"} - if prefix := gccgoPrefix(p); prefix != "" { - gcargs = append(gcargs, "-fgo-prefix="+gccgoPrefix(p)) + gcargs = append(gcargs, b.gccArchArgs()...) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath) + } + if p.localPrefix != "" { + gcargs = append(gcargs, "-fgo-relative-import-path="+p.localPrefix) } args := stringList("gccgo", importArgs, "-c", gcargs, "-o", ofile, buildGccgoflags) for _, f := range gofiles { args = append(args, mkAbs(p.Dir, f)) } - return ofile, b.run(p.Dir, p.ImportPath, args) + + output, err = b.runOut(p.Dir, p.ImportPath, args) + return ofile, output, err } -func (gccgcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { +func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + defs = append(defs, b.gccArchArgs()...) + return b.run(p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, defs, sfile) } -func (gccgcToolchain) pkgpath(basedir string, p *Package) string { +func (gccgoToolchain) pkgpath(basedir string, p *Package) string { end := filepath.FromSlash(p.ImportPath + ".a") afile := filepath.Join(basedir, end) // add "lib" to the final element return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) } -func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { +func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles []string) error { var absOfiles []string for _, f := range ofiles { absOfiles = append(absOfiles, mkAbs(objDir, f)) @@ -1329,12 +1545,14 @@ func (gccgcToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles return b.run(p.Dir, p.ImportPath, "ar", "cru", mkAbs(objDir, afile), absOfiles) } -func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { +func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error { // gccgo needs explicit linking with all package dependencies, // and all LDFLAGS from cgo dependencies. afiles := make(map[*Package]string) - ldflags := []string{} + sfiles := make(map[*Package][]string) + ldflags := b.gccArchArgs() cgoldflags := []string{} + usesCgo := false for _, a := range allactions { if a.p != nil { if !a.p.Standard { @@ -1343,31 +1561,99 @@ func (tools gccgcToolchain) ld(b *builder, p *Package, out string, allactions [] } } cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...) + if len(a.p.CgoFiles) > 0 { + usesCgo = true + } + if a.p.usesSwig() { + sd := a.p.swigDir(&buildContext) + for _, f := range stringList(a.p.SwigFiles, a.p.SwigCXXFiles) { + soname := a.p.swigSoname(f) + sfiles[a.p] = append(sfiles[a.p], filepath.Join(sd, soname)) + } + usesCgo = true + } } } for _, afile := range afiles { ldflags = append(ldflags, afile) } + for _, sfiles := range sfiles { + ldflags = append(ldflags, sfiles...) + } ldflags = append(ldflags, cgoldflags...) - return b.run(".", p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(", ldflags, "-Wl,-)") + if usesCgo && goos == "linux" { + ldflags = append(ldflags, "-Wl,-E") + } + return b.run(".", p.ImportPath, "gccgo", "-o", out, ofiles, "-Wl,-(", ldflags, "-Wl,-)", buildGccgoflags) } -func (gccgcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { +func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error { inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) cfile = mkAbs(p.Dir, cfile) + defs := []string{"-D", "GOOS_" + goos, "-D", "GOARCH_" + goarch} + defs = append(defs, b.gccArchArgs()...) + if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { + defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) + } + // TODO: Support using clang here (during gccgo build)? return b.run(p.Dir, p.ImportPath, "gcc", "-Wall", "-g", - "-I", objdir, "-I", inc, "-o", ofile, - "-DGOOS_"+goos, "-DGOARCH_"+goarch, "-c", cfile) + "-I", objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) } -func gccgoPrefix(p *Package) string { - switch { - case p.build.IsCommand() && !p.forceLibrary: +func gccgoPkgpath(p *Package) string { + if p.build.IsCommand() && !p.forceLibrary { return "" - case p.fake: - return "fake_" + p.ImportPath } - return "go_" + p.ImportPath + return p.ImportPath +} + +func gccgoCleanPkgpath(p *Package) string { + clean := func(r rune) rune { + switch { + case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', + '0' <= r && r <= '9': + return r + } + return '_' + } + return strings.Map(clean, gccgoPkgpath(p)) +} + +// libgcc returns the filename for libgcc, as determined by invoking gcc with +// the -print-libgcc-file-name option. +func (b *builder) libgcc(p *Package) (string, error) { + var buf bytes.Buffer + + gccCmd := b.gccCmd(p.Dir) + + prev := b.print + if buildN { + // In -n mode we temporarily swap out the builder's + // print function to capture the command-line. This + // let's us assign it to $LIBGCC and produce a valid + // buildscript for cgo packages. + b.print = func(a ...interface{}) (n int, err error) { + return fmt.Fprint(&buf, a...) + } + } + f, err := b.runOut(p.Dir, p.ImportPath, gccCmd, "-print-libgcc-file-name") + if err != nil { + return "", fmt.Errorf("gcc -print-libgcc-file-name: %v (%s)", err, f) + } + if buildN { + s := fmt.Sprintf("LIBGCC=$(%s)\n", buf.Next(buf.Len()-1)) + b.print = prev + b.print(s) + return "$LIBGCC", nil + } + + // clang might not be able to find libgcc, and in that case, + // it will simply return "libgcc.a", which is of no use to us. + if strings.Contains(gccCmd[0], "clang") && !filepath.IsAbs(string(f)) { + return "", nil + } + + return strings.Trim(string(f), "\r\n"), nil } // gcc runs the gcc C compiler to create an object from a single C file. @@ -1386,20 +1672,19 @@ func (b *builder) gccCmd(objdir string) []string { // NOTE: env.go's mkEnv knows that the first three // strings returned are "gcc", "-I", objdir (and cuts them off). - // TODO: HOST_CC? - a := []string{"gcc", "-I", objdir, "-g", "-O2"} + gcc := strings.Fields(os.Getenv("CC")) + if len(gcc) == 0 { + gcc = append(gcc, "gcc") + } + a := []string{gcc[0], "-I", objdir, "-g", "-O2"} + a = append(a, gcc[1:]...) // Definitely want -fPIC but on Windows gcc complains // "-fPIC ignored for target (all code is position independent)" if goos != "windows" { a = append(a, "-fPIC") } - switch archChar { - case "8": - a = append(a, "-m32") - case "6": - a = append(a, "-m64") - } + a = append(a, b.gccArchArgs()...) // gcc-4.5 and beyond require explicit "-pthread" flag // for multithreading with pthread library. if buildContext.CgoEnabled { @@ -1411,6 +1696,11 @@ func (b *builder) gccCmd(objdir string) []string { } } + // clang is too smart about command-line arguments + if strings.Contains(a[0], "clang") { + a = append(a, "-Qunused-arguments") + } + // On OS X, some of the compilers behave as if -fno-common // is always set, and the Mach-O linker in 6l/8l assumes this. // See http://golang.org/issue/3253. @@ -1421,12 +1711,31 @@ func (b *builder) gccCmd(objdir string) []string { return a } +// gccArchArgs returns arguments to pass to gcc based on the architecture. +func (b *builder) gccArchArgs() []string { + switch archChar { + case "8": + return []string{"-m32"} + case "6": + return []string{"-m64"} + case "5": + return []string{"-marm"} // not thumb + } + return nil +} + func envList(key string) []string { return strings.Fields(os.Getenv(key)) } var cgoRe = regexp.MustCompile(`[/\\:]`) +var ( + cgoLibGccFile string + cgoLibGccErr error + cgoLibGccFileOnce sync.Once +) + func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) { if goos != toolGOOS { return nil, nil, errors.New("cannot use cgo when compiling for a different operating system") @@ -1478,10 +1787,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, if p.Standard && p.ImportPath == "runtime/cgo" { cgoflags = append(cgoflags, "-import_runtime_cgo=false") } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if p.Standard && (p.ImportPath == "runtime/race" || p.ImportPath == "runtime/cgo") { + cgoflags = append(cgoflags, "-import_syscall=false") + } + + if _, ok := buildToolchain.(gccgoToolchain); ok { cgoflags = append(cgoflags, "-gccgo") - if prefix := gccgoPrefix(p); prefix != "" { - cgoflags = append(cgoflags, "-gccgoprefix="+gccgoPrefix(p)) + if pkgpath := gccgoPkgpath(p); pkgpath != "" { + cgoflags = append(cgoflags, "-gccgopkgpath="+pkgpath) } objExt = "o" } @@ -1499,6 +1812,36 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, // gcc var linkobj []string + + var bareLDFLAGS []string + // filter out -lsomelib, and -framework X if on Darwin + for i := 0; i < len(cgoLDFLAGS); i++ { + f := cgoLDFLAGS[i] + if !strings.HasPrefix(f, "-l") { + if goos == "darwin" && f == "-framework" { // skip the -framework X + i += 1 + continue + } + bareLDFLAGS = append(bareLDFLAGS, f) + } + } + + cgoLibGccFileOnce.Do(func() { + cgoLibGccFile, cgoLibGccErr = b.libgcc(p) + }) + if cgoLibGccFile == "" && cgoLibGccErr != nil { + return nil, nil, err + } + + var staticLibs []string + if goos == "windows" { + // libmingw32 and libmingwex might also use libgcc, so libgcc must come last + staticLibs = []string{"-lmingwex", "-lmingw32"} + } + if cgoLibGccFile != "" { + staticLibs = append(staticLibs, cgoLibGccFile) + } + for _, cfile := range cfiles { ofile := obj + cfile[:len(cfile)-1] + "o" if err := b.gcc(p, ofile, cgoCFLAGS, obj+cfile); err != nil { @@ -1517,19 +1860,30 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, linkobj = append(linkobj, ofile) outObj = append(outObj, ofile) } + linkobj = append(linkobj, p.SysoFiles...) dynobj := obj + "_cgo_.o" + if goarch == "arm" && goos == "linux" { // we need to use -pie for Linux/ARM to get accurate imported sym + cgoLDFLAGS = append(cgoLDFLAGS, "-pie") + } if err := b.gccld(p, dynobj, cgoLDFLAGS, linkobj); err != nil { return nil, nil, err } + if goarch == "arm" && goos == "linux" { // but we don't need -pie for normal cgo programs + cgoLDFLAGS = cgoLDFLAGS[0 : len(cgoLDFLAGS)-1] + } - if _, ok := buildToolchain.(gccgcToolchain); ok { + if _, ok := buildToolchain.(gccgoToolchain); ok { // we don't use dynimport when using gccgo. return outGo, outObj, nil } // cgo -dynimport importC := obj + "_cgo_import.c" - if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC); err != nil { + cgoflags = []string{} + if p.Standard && p.ImportPath == "runtime/cgo" { + cgoflags = append(cgoflags, "-dynlinker") // record path to dynamic linker + } + if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport", dynobj, "-dynout", importC, cgoflags); err != nil { return nil, nil, err } @@ -1539,14 +1893,162 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, return nil, nil, err } + ofile := obj + "_all.o" + var gccObjs, nonGccObjs []string + for _, f := range outObj { + if strings.HasSuffix(f, ".o") { + gccObjs = append(gccObjs, f) + } else { + nonGccObjs = append(nonGccObjs, f) + } + } + if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil { + return nil, nil, err + } + // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows // must be processed before the gcc-generated objects. // Put it first. http://golang.org/issue/2601 - outObj = append([]string{importObj}, outObj...) + outObj = stringList(importObj, nonGccObjs, ofile) return outGo, outObj, nil } +// Run SWIG on all SWIG input files. +func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) { + + intgosize, err := b.swigIntSize(obj) + if err != nil { + return nil, nil, err + } + + for _, f := range p.SwigFiles { + goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + for _, f := range p.SwigCXXFiles { + goFile, objFile, err := b.swigOne(p, f, obj, true, intgosize) + if err != nil { + return nil, nil, err + } + if goFile != "" { + outGo = append(outGo, goFile) + } + if objFile != "" { + outObj = append(outObj, objFile) + } + } + return outGo, outObj, nil +} + +// This code fails to build if sizeof(int) <= 32 +const swigIntSizeCode = ` +package main +const i int = 1 << 32 +` + +// Determine the size of int on the target system for the -intgosize option +// of swig >= 2.0.9 +func (b *builder) swigIntSize(obj string) (intsize string, err error) { + src := filepath.Join(b.work, "swig_intsize.go") + if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil { + return + } + srcs := []string{src} + + p := goFilesPackage(srcs) + + if _, _, e := buildToolchain.gc(b, p, obj, nil, srcs); e != nil { + return "32", nil + } + return "64", nil +} + +// Run SWIG on one SWIG input file. +func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize string) (outGo, outObj string, err error) { + n := 5 // length of ".swig" + if cxx { + n = 8 // length of ".swigcxx" + } + base := file[:len(file)-n] + goFile := base + ".go" + cBase := base + "_gc." + gccBase := base + "_wrap." + gccExt := "c" + if cxx { + gccExt = "cxx" + } + soname := p.swigSoname(file) + + _, gccgo := buildToolchain.(gccgoToolchain) + + // swig + args := []string{ + "-go", + "-intgosize", intgosize, + "-module", base, + "-soname", soname, + "-o", obj + gccBase + gccExt, + "-outdir", obj, + } + if gccgo { + args = append(args, "-gccgo") + } + if cxx { + args = append(args, "-c++") + } + + if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil { + if len(out) > 0 { + if bytes.Contains(out, []byte("Unrecognized option -intgosize")) { + return "", "", errors.New("must have SWIG version >= 2.0.9\n") + } + b.showOutput(p.Dir, p.ImportPath, b.processOutput(out)) + return "", "", errPrintedOutput + } + return "", "", err + } + + var cObj string + if !gccgo { + // cc + cObj = obj + cBase + archChar + if err := buildToolchain.cc(b, p, obj, cObj, obj+cBase+"c"); err != nil { + return "", "", err + } + } + + // gcc + gccObj := obj + gccBase + "o" + if err := b.gcc(p, gccObj, []string{"-g", "-fPIC", "-O2"}, obj+gccBase+gccExt); err != nil { + return "", "", err + } + + // create shared library + osldflags := map[string][]string{ + "darwin": {"-dynamiclib", "-Wl,-undefined,dynamic_lookup"}, + "freebsd": {"-shared", "-lpthread", "-lm"}, + "linux": {"-shared", "-lpthread", "-lm"}, + "windows": {"-shared", "-lm", "-mthreads"}, + } + var cxxlib []string + if cxx { + cxxlib = []string{"-lstdc++"} + } + ldflags := stringList(osldflags[goos], cxxlib) + b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags) + + return obj + goFile, cObj, nil +} + // An actionQueue is a priority queue of actions. type actionQueue []*action @@ -1569,3 +2071,18 @@ func (q *actionQueue) push(a *action) { func (q *actionQueue) pop() *action { return heap.Pop(q).(*action) } + +func raceInit() { + if !buildRace { + return + } + if goarch != "amd64" || goos != "linux" && goos != "darwin" && goos != "windows" { + fmt.Fprintf(os.Stderr, "go %s: -race is only supported on linux/amd64, darwin/amd64 and windows/amd64\n", flag.Args()[0]) + os.Exit(2) + } + buildGcflags = append(buildGcflags, "-race") + buildLdflags = append(buildLdflags, "-race") + buildCcflags = append(buildCcflags, "-D", "RACE") + buildContext.InstallTag = "race" + buildContext.BuildTags = append(buildContext.BuildTags, "race") +} diff --git a/src/cmd/go/clean.go b/src/cmd/go/clean.go index 773951826..ba600d3bb 100644 --- a/src/cmd/go/clean.go +++ b/src/cmd/go/clean.go @@ -34,6 +34,7 @@ source directories corresponding to the import paths: 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 @@ -93,11 +94,12 @@ var cleanFile = map[string]bool{ } var cleanExt = map[string]bool{ - ".5": true, - ".6": true, - ".8": true, - ".a": true, - ".o": true, + ".5": true, + ".6": true, + ".8": true, + ".a": true, + ".o": true, + ".so": true, } func clean(p *Package) { @@ -168,7 +170,9 @@ func clean(p *Package) { continue } } - os.RemoveAll(filepath.Join(p.Dir, name)) + if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil { + errorf("go clean: %v", err) + } } continue } @@ -178,7 +182,7 @@ func clean(p *Package) { } if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] { - os.Remove(filepath.Join(p.Dir, name)) + removeFile(filepath.Join(p.Dir, name)) } } @@ -187,7 +191,21 @@ func clean(p *Package) { b.showcmd("", "rm -f %s", p.target) } if !cleanN { - os.Remove(p.target) + removeFile(p.target) + } + } + + if cleanI && p.usesSwig() { + for _, f := range stringList(p.SwigFiles, p.SwigCXXFiles) { + dir := p.swigDir(&buildContext) + soname := p.swigSoname(f) + target := filepath.Join(dir, soname) + if cleanN || cleanX { + b.showcmd("", "rm -f %s", target) + } + if !cleanN { + removeFile(target) + } } } @@ -197,3 +215,11 @@ func clean(p *Package) { } } } + +// removeFile tries to remove file f, if error other than file doesn't exist +// occurs, it will report the error. +func removeFile(f string) { + if err := os.Remove(f); err != nil && !os.IsNotExist(err) { + errorf("go clean: %v", err) + } +} diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 5e7b10692..0297b7602 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -76,7 +76,12 @@ The build flags are shared by the build, install, run, and test commands: do not delete it when exiting. -x print the commands. + -race + enable data race detection. + Supported only on linux/amd64, darwin/amd64 and windows/amd64. + -ccflags 'arg list' + arguments to pass on each 5c, 6c, or 8c compiler invocation -compiler name name of compiler to use, as in runtime.Compiler (gccgo or gc) -gccgoflags 'arg list' @@ -90,6 +95,9 @@ The build flags are shared by the build, install, run, and test commands: See the documentation for the go/build package for more information about build tags. +The list flags accept a space-separated list of strings. To embed spaces +in an element in the list, surround it with either single or double quotes. + For more about specifying packages, see 'go help packages'. For more about where packages and binaries are installed, see 'go help gopath'. @@ -121,6 +129,7 @@ source directories corresponding to the import paths: 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 @@ -215,14 +224,11 @@ Download and install packages and dependencies Usage: - go get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages] + go get [-d] [-fix] [-u] [build flags] [packages] Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -233,6 +239,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -266,7 +275,7 @@ List packages Usage: - go list [-e] [-f format] [-json] [packages] + go list [-e] [-f format] [-json] [-tags 'tag list'] [packages] List lists the packages named by the import paths, one per line. @@ -276,10 +285,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -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: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -293,12 +302,15 @@ being passed to the template is: Root string // Go root or Go path dir containing this package // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go sources files that import "C" - CFiles []string // .c source files - HFiles []string // .h source files - SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + IgnoredGoFiles []string // .go sources ignored due to build constraints + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler @@ -333,6 +345,9 @@ printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed). +The -tags flag specifies a list of build tags, like in the 'go build' +command. + For more about specifying packages, see 'go help packages'. @@ -368,6 +383,7 @@ 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. +Each listed package causes the execution of a separate test binary. By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. @@ -445,7 +461,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: @@ -491,7 +507,7 @@ Here's an example directory layout: 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 +but new packages are always downloaded into the first directory in the list. @@ -509,13 +525,13 @@ denotes the package in that directory. Otherwise, the import path P denotes the package found in the directory DIR/src/P for some DIR listed in the GOPATH -environment variable (see 'go help gopath'). +environment variable (see 'go help gopath'). If no import paths are given, the action applies to the package in the current directory. The special import path "all" expands to all package directories -found in all the GOPATH trees. For example, 'go list all' +found in all the GOPATH trees. For example, 'go list all' lists all the packages on the local system. The special import path "std" is like all but expands to just the @@ -623,7 +639,7 @@ The meta tag has the form: <meta name="go-import" content="import-prefix vcs repo-root"> -The import-prefix is the import path correponding to the repository +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 fetched with "go get". If it's not an exact match, another http request is made at the prefix to verify the <meta> tags match. @@ -663,28 +679,47 @@ Description of testing flags The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: - - -test.v - Verbose output: log all tests as they are run. +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -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). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + 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. + + -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. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -692,32 +727,35 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -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. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -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. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.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. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update @@ -725,6 +763,11 @@ will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update +The test flags that generate profiles also leave the test binary in pkg.test +for use when analyzing the profiles. + +Flags not recognized by 'go test' must be placed after any specified packages. + Description of testing functions @@ -740,8 +783,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, func BenchmarkXXX(b *testing.B) { ... } -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 and os.Stderr. +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. That output is compared against the function's "Output:" comment, which must be the last comment in the function body (see example below). An example with no such comment, or with no text after "Output:" is compiled @@ -769,6 +812,6 @@ See the documentation of the testing package for more information. */ -package documentation +package main // NOTE: cmdDoc is in fmt.go. diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go index d5b034809..6cab37b48 100644 --- a/src/cmd/go/env.go +++ b/src/cmd/go/env.go @@ -6,6 +6,7 @@ package main import ( "fmt" + "os" "runtime" "strings" ) @@ -33,16 +34,22 @@ func mkEnv() []envVar { b.init() env := []envVar{ - {"GOROOT", goroot}, - {"GOBIN", gobin}, {"GOARCH", goarch}, + {"GOBIN", gobin}, {"GOCHAR", archChar}, - {"GOOS", goos}, {"GOEXE", exeSuffix}, {"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTOS", runtime.GOOS}, + {"GOOS", goos}, + {"GOPATH", os.Getenv("GOPATH")}, + {"GOROOT", goroot}, {"GOTOOLDIR", toolDir}, - {"GOGCCFLAGS", strings.Join(b.gccCmd(".")[3:], " ")}, + } + + if goos != "plan9" { + cmd := b.gccCmd(".") + env = append(env, envVar{"CC", cmd[0]}) + env = append(env, envVar{"GOGCCFLAGS", strings.Join(cmd[3:], " ")}) } if buildContext.CgoEnabled { diff --git a/src/cmd/go/fix.go b/src/cmd/go/fix.go index ef02b5739..8736cce3e 100644 --- a/src/cmd/go/fix.go +++ b/src/cmd/go/fix.go @@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) { // Use pkg.gofiles instead of pkg.Dir so that // the command only applies to this package, // not to packages in subdirectories. - run(stringList(tool("fix"), relPaths(pkg.gofiles))) + run(stringList(tool("fix"), relPaths(pkg.allgofiles))) } } diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go index b1aba32f3..9d3c911dd 100644 --- a/src/cmd/go/fmt.go +++ b/src/cmd/go/fmt.go @@ -34,7 +34,7 @@ func runFmt(cmd *Command, args []string) { // Use pkg.gofiles instead of pkg.Dir so that // the command only applies to this package, // not to packages in subdirectories. - run(stringList("gofmt", "-l", "-w", relPaths(pkg.gofiles))) + run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles))) } } diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index fe45697e2..8c08ab261 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -18,15 +18,12 @@ import ( ) var cmdGet = &Command{ - UsageLine: "get [-a] [-d] [-fix] [-n] [-p n] [-u] [-v] [-x] [packages]", + UsageLine: "get [-d] [-fix] [-u] [build flags] [packages]", Short: "download and install packages and dependencies", Long: ` Get downloads and installs the packages named by the import paths, along with their dependencies. -The -a, -n, -v, -x, and -p flags have the same meaning as in 'go build' -and 'go install'. See 'go help build'. - The -d flag instructs get to stop after downloading the packages; that is, it instructs get not to install the packages. @@ -37,6 +34,9 @@ The -u flag instructs get to use the network to update the named packages and their dependencies. By default, get uses the network to check out missing packages but does not use it to look for updates to existing packages. +Get also accepts all the flags in the 'go build' and 'go install' commands, +to control the installation. See 'go help build'. + When checking out or updating a package, get looks for a branch or tag that matches the locally installed version of Go. The most important rule is that if the local installation is running version "go1", get @@ -93,7 +93,7 @@ func runGet(cmd *Command, args []string) { runInstall(cmd, args) } -// downloadPath prepares the list of paths to pass to download. +// downloadPaths prepares the list of paths to pass to download. // It expands ... patterns that can be expanded. If there is no match // for a particular pattern, downloadPaths leaves it in the result list, // in the hope that we can figure out the repository from the @@ -105,7 +105,7 @@ func downloadPaths(args []string) []string { if strings.Contains(a, "...") { var expand []string // Use matchPackagesInFS to avoid printing - // warnings. They will be printed by the + // warnings. They will be printed by the // eventual call to importPaths instead. if build.IsLocalImport(a) { expand = matchPackagesInFS(a) @@ -204,7 +204,7 @@ func download(arg string, stk *importStack) { // due to wildcard expansion. for _, p := range pkgs { if *getFix { - run(stringList(tool("fix"), relPaths(p.gofiles))) + run(stringList(tool("fix"), relPaths(p.allgofiles))) // The imports might have changed, so reload again. p = reloadPackage(arg, stk) @@ -247,16 +247,17 @@ func downloadPackage(p *Package) error { } if p.build.SrcRoot == "" { - // Package not found. Put in first directory of $GOPATH or else $GOROOT. - // Guard against people setting GOPATH=$GOROOT. We have to use - // $GOROOT's directory hierarchy (src/pkg, not just src) in that case. - if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot { - p.build.SrcRoot = filepath.Join(list[0], "src") - p.build.PkgRoot = filepath.Join(list[0], "pkg") - } else { - p.build.SrcRoot = filepath.Join(goroot, "src", "pkg") - p.build.PkgRoot = filepath.Join(goroot, "pkg") + // Package not found. Put in first directory of $GOPATH. + list := filepath.SplitList(buildContext.GOPATH) + if len(list) == 0 { + return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath") + } + // Guard against people setting GOPATH=$GOROOT. + if list[0] == goroot { + return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath") } + p.build.SrcRoot = filepath.Join(list[0], "src") + p.build.PkgRoot = filepath.Join(list[0], "pkg") } root := filepath.Join(p.build.SrcRoot, rootPath) // If we've considered this repository already, don't do it again. diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go index 7539753af..6d2bd7dbb 100644 --- a/src/cmd/go/help.go +++ b/src/cmd/go/help.go @@ -186,7 +186,7 @@ On Unix, the value is a colon-separated string. On Windows, the value is a semicolon-separated string. On Plan 9, the value is a list. -GOPATH must be set to build and install packages outside the +GOPATH must be set to get, build and install packages outside the standard Go tree. Each directory listed in GOPATH must have a prescribed structure: diff --git a/src/cmd/go/http.go b/src/cmd/go/http.go index 6de9a3e1e..107b820f2 100644 --- a/src/cmd/go/http.go +++ b/src/cmd/go/http.go @@ -20,9 +20,13 @@ import ( "net/url" ) +// httpClient is the default HTTP client, but a variable so it can be +// changed by tests, without modifying http.DefaultClient. +var httpClient = http.DefaultClient + // httpGET returns the data from an HTTP GET request for the given URL. func httpGET(url string) ([]byte, error) { - resp, err := http.Get(url) + resp, err := httpClient.Get(url) if err != nil { return nil, err } @@ -37,10 +41,6 @@ func httpGET(url string) ([]byte, error) { return b, nil } -// httpClient is the default HTTP client, but a variable so it can be -// changed by tests, without modifying http.DefaultClient. -var httpClient = http.DefaultClient - // httpsOrHTTP returns the body of either the importPath's // https resource or, if unavailable, the http resource. func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go index edb59aa79..2d23d077e 100644 --- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -9,11 +9,12 @@ import ( "encoding/json" "io" "os" + "strings" "text/template" ) var cmdList = &Command{ - UsageLine: "list [-e] [-f format] [-json] [packages]", + UsageLine: "list [-e] [-f format] [-json] [-tags 'tag list'] [packages]", Short: "list packages", Long: ` List lists the packages named by the import paths, one per line. @@ -24,10 +25,10 @@ The default output shows the package import path: code.google.com/p/goauth2/oauth code.google.com/p/sqlite -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: +The -f flag specifies an alternate format for the list, using the +syntax of package template. The default output is equivalent to -f +'{{.ImportPath}}'. One extra template function is available, "join", +which calls strings.Join. The struct being passed to the template is: type Package struct { Dir string // directory containing package sources @@ -41,12 +42,15 @@ being passed to the template is: Root string // Go root or Go path dir containing this package // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string // .go sources files that import "C" - CFiles []string // .c source files - HFiles []string // .h source files - SFiles []string // .s source files - SysoFiles []string // .syso object files to add to archive + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string // .go sources files that import "C" + IgnoredGoFiles []string // .go sources ignored due to build constraints + CFiles []string // .c source files + HFiles []string // .h source files + SFiles []string // .s source files + SysoFiles []string // .syso object files to add to archive + SwigFiles []string // .swig files + SwigCXXFiles []string // .swigcxx files // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler @@ -81,6 +85,9 @@ printing. Erroneous packages will have a non-empty ImportPath and a non-nil Error field; other information may or may not be missing (zeroed). +The -tags flag specifies a list of build tags, like in the 'go build' +command. + For more about specifying packages, see 'go help packages'. `, } @@ -88,6 +95,7 @@ For more about specifying packages, see 'go help packages'. func init() { cmdList.Run = runList // break init cycle cmdList.Flag.Var(buildCompiler{}, "compiler", "") + cmdList.Flag.Var((*stringsFlag)(&buildContext.BuildTags), "tags", "") } var listE = cmdList.Flag.Bool("e", false, "") @@ -96,7 +104,7 @@ var listJson = cmdList.Flag.Bool("json", false, "") var nl = []byte{'\n'} func runList(cmd *Command, args []string) { - out := newCountingWriter(os.Stdout) + out := newTrackingWriter(os.Stdout) defer out.w.Flush() var do func(*Package) @@ -111,18 +119,17 @@ func runList(cmd *Command, args []string) { out.Write(nl) } } else { - tmpl, err := template.New("main").Parse(*listFmt) + tmpl, err := template.New("main").Funcs(template.FuncMap{"join": strings.Join}).Parse(*listFmt) if err != nil { fatalf("%s", err) } do = func(p *Package) { - out.Reset() if err := tmpl.Execute(out, p); err != nil { out.Flush() fatalf("%s", err) } - if out.Count() > 0 { - out.w.WriteRune('\n') + if out.NeedNL() { + out.Write([]byte{'\n'}) } } } @@ -137,32 +144,33 @@ func runList(cmd *Command, args []string) { } } -// CountingWriter counts its data, so we can avoid appending a newline -// if there was no actual output. -type CountingWriter struct { - w *bufio.Writer - count int64 +// TrackingWriter tracks the last byte written on every write so +// we can avoid printing a newline if one was already written or +// if there is no output at all. +type TrackingWriter struct { + w *bufio.Writer + last byte } -func newCountingWriter(w io.Writer) *CountingWriter { - return &CountingWriter{ - w: bufio.NewWriter(w), +func newTrackingWriter(w io.Writer) *TrackingWriter { + return &TrackingWriter{ + w: bufio.NewWriter(w), + last: '\n', } } -func (cw *CountingWriter) Write(p []byte) (n int, err error) { - cw.count += int64(len(p)) - return cw.w.Write(p) -} - -func (cw *CountingWriter) Flush() { - cw.w.Flush() +func (t *TrackingWriter) Write(p []byte) (n int, err error) { + n, err = t.w.Write(p) + if n > 0 { + t.last = p[n-1] + } + return } -func (cw *CountingWriter) Reset() { - cw.count = 0 +func (t *TrackingWriter) Flush() { + t.w.Flush() } -func (cw *CountingWriter) Count() int64 { - return cw.count +func (t *TrackingWriter) NeedNL() bool { + return t.last != '\n' } diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 20585d1be..bf1dad40f 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -127,6 +127,17 @@ func main() { // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) + } else { + for _, p := range filepath.SplitList(gopath) { + if strings.Contains(p, "~") && runtime.GOOS != "windows" { + fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p) + os.Exit(2) + } + if build.IsLocalImport(p) { + fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) + os.Exit(2) + } + } } for _, cmd := range commands { @@ -189,7 +200,7 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser {{end}}*/ -package documentation +package main // NOTE: cmdDoc is in fmt.go. ` @@ -368,6 +379,25 @@ func runOut(dir string, cmdargs ...interface{}) []byte { return out } +// envForDir returns a copy of the environment +// suitable for running in the given directory. +// The environment is the current process's environment +// but with an updated $PWD, so that an os.Getwd in the +// child will be faster. +func envForDir(dir string) []string { + env := os.Environ() + for i, kv := range env { + if strings.HasPrefix(kv, "PWD=") { + env[i] = "PWD=" + dir + return env + } + } + // Internally we only use rooted paths, so dir is rooted. + // Even if dir is not rooted, no harm done. + env = append(env, "PWD="+dir) + return env +} + // matchPattern(pattern)(name) reports whether // name matches pattern. Pattern is a limited glob // pattern in which '...' means 'any string' and there @@ -423,19 +453,20 @@ func matchPackages(pattern string) []string { return filepath.SkipDir } - _, err = build.ImportDir(path, 0) - if err != nil { - return nil - } - // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. name = "cmd/" + name - if !have[name] { - have[name] = true - if match(name) { - pkgs = append(pkgs, name) - } + if have[name] { + return nil } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + return nil + } + pkgs = append(pkgs, name) return nil }) @@ -463,14 +494,14 @@ func matchPackages(pattern string) []string { return nil } have[name] = true - - _, err = build.ImportDir(path, 0) - if err != nil && strings.Contains(err.Error(), "no Go source files") { + if !match(name) { return nil } - if match(name) { - pkgs = append(pkgs, name) + _, err = buildContext.ImportDir(path, 0) + if err != nil && strings.Contains(err.Error(), "no Go source files") { + return nil } + pkgs = append(pkgs, name) return nil }) } @@ -559,3 +590,60 @@ func stringList(args ...interface{}) []string { } return x } + +// 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 +// have the desired property in some corner cases. +func toFold(s string) string { + // Fast path: all ASCII, no upper case. + // Most paths look like this already. + for i := 0; i < len(s); i++ { + c := s[i] + if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' { + goto Slow + } + } + return s + +Slow: + var buf bytes.Buffer + for _, r := range s { + // SimpleFold(x) cycles to the next equivalent rune > x + // or wraps around to smaller values. Iterate until it wraps, + // and we've found the minimum value. + for { + r0 := r + r = unicode.SimpleFold(r0) + if r <= r0 { + break + } + } + // Exception to allow fast path above: A-Z => a-z + if 'A' <= r && r <= 'Z' { + r += 'a' - 'A' + } + buf.WriteRune(r) + } + return buf.String() +} + +// foldDup reports a pair of strings from the list that are +// equal according to strings.EqualFold. +// It returns "", "" if there are no such strings. +func foldDup(list []string) (string, string) { + clash := map[string]string{} + for _, s := range list { + fold := toFold(s) + if t := clash[fold]; t != "" { + if s > t { + s, t = t, s + } + return s, t + } + clash[fold] = s + } + return "", "" +} diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index 30bbfad55..7fc61fd86 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -36,12 +36,15 @@ type Package struct { Root string `json:",omitempty"` // Go root or Go path dir containing this package // Source files - GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) - CgoFiles []string `json:",omitempty"` // .go sources files that import "C" - CFiles []string `json:",omitempty"` // .c source files - HFiles []string `json:",omitempty"` // .h source files - SFiles []string `json:",omitempty"` // .s source files - SysoFiles []string `json:",omitempty"` // .syso system object files added to package + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) + CgoFiles []string `json:",omitempty"` // .go sources files that import "C" + IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints + CFiles []string `json:",omitempty"` // .c source files + HFiles []string `json:",omitempty"` // .h source files + SFiles []string `json:",omitempty"` // .s source files + SysoFiles []string `json:",omitempty"` // .syso system object files added to package + SwigFiles []string `json:",omitempty"` // .swig files + SwigCXXFiles []string `json:",omitempty"` // .swigcxx files // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -69,12 +72,14 @@ type Package struct { imports []*Package deps []*Package gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths + allgofiles []string // gofiles + IgnoredGoFiles, absolute paths target string // installed file for this package (may be executable) fake bool // synthesized package forceBuild bool // this package must be rebuilt forceLibrary bool // this package is a library (even if named "main") local bool // imported via local path (./ or ../) localPrefix string // interpret ./ and ../ imports relative to this prefix + exeName string // desired name for temporary executable } func (p *Package) copyBuild(pp *build.Package) { @@ -90,10 +95,13 @@ func (p *Package) copyBuild(pp *build.Package) { p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") p.GoFiles = pp.GoFiles p.CgoFiles = pp.CgoFiles + p.IgnoredGoFiles = pp.IgnoredGoFiles p.CFiles = pp.CFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles p.SysoFiles = pp.SysoFiles + p.SwigFiles = pp.SwigFiles + p.SwigCXXFiles = pp.SwigCXXFiles p.CgoCFLAGS = pp.CgoCFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig @@ -117,6 +125,9 @@ func (p *PackageError) Error() string { // is the most important thing. return p.Pos + ": " + p.Err } + if len(p.ImportStack) == 0 { + return p.Err + } return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err } @@ -246,7 +257,7 @@ func reusePackage(p *Package, stk *importStack) *Package { if p.Error == nil { p.Error = &PackageError{ ImportStack: stk.copy(), - Err: "import loop", + Err: "import cycle not allowed", } } p.Incomplete = true @@ -260,13 +271,11 @@ func reusePackage(p *Package, stk *importStack) *Package { // isGoTool is the list of directories for Go programs that are installed in // $GOROOT/pkg/tool. var isGoTool = map[string]bool{ - "cmd/api": true, - "cmd/cgo": true, - "cmd/fix": true, - "cmd/vet": true, - "cmd/yacc": true, - "exp/gotype": true, - "exp/ebnflint": true, + "cmd/api": true, + "cmd/cgo": true, + "cmd/fix": true, + "cmd/vet": true, + "cmd/yacc": true, } // expandScanner expands a scanner.List error into all the errors in the list. @@ -316,11 +325,13 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Install cross-compiled binaries to subdirectories of bin. elem = full } - p.target = filepath.Join(p.build.BinDir, elem) - if p.Goroot && isGoTool[p.ImportPath] { + if p.build.BinDir != "" { + p.target = filepath.Join(p.build.BinDir, elem) + } + if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { p.target = filepath.Join(gorootPkg, "tool", full) } - if buildContext.GOOS == "windows" { + if p.target != "" && buildContext.GOOS == "windows" { p.target += ".exe" } } else if p.local { @@ -340,6 +351,11 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") + // When race detection enabled everything depends on runtime/race. + // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies. + if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) { + importPaths = append(importPaths, "runtime/race") + } } // Build list of full paths to all Go files in the package, @@ -350,6 +366,38 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } sort.Strings(p.gofiles) + p.allgofiles = stringList(p.IgnoredGoFiles) + for i := range p.allgofiles { + p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i]) + } + p.allgofiles = append(p.allgofiles, p.gofiles...) + sort.Strings(p.allgofiles) + + // Check for case-insensitive collision of input files. + // To avoid problems on case-insensitive files, we reject any package + // where two different input files have equal names under a case-insensitive + // comparison. + f1, f2 := foldDup(stringList( + p.GoFiles, + p.CgoFiles, + p.IgnoredGoFiles, + p.CFiles, + p.HFiles, + p.SFiles, + p.SysoFiles, + p.SwigFiles, + p.SwigCXXFiles, + p.TestGoFiles, + p.XTestGoFiles, + )) + if f1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive file name collision: %q and %q", f1, f2), + } + return p + } + // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) @@ -403,11 +451,47 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") { p.target = "" } - p.Target = p.target + + // In the absence of errors lower in the dependency tree, + // check for case-insensitive collisions of import paths. + if len(p.DepsErrors) == 0 { + dep1, dep2 := foldDup(p.Deps) + if dep1 != "" { + p.Error = &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2), + } + return p + } + } + return p } +// usesSwig returns whether the package needs to run SWIG. +func (p *Package) usesSwig() bool { + return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0 +} + +// swigSoname returns the name of the shared library we create for a +// SWIG input file. +func (p *Package) swigSoname(file string) string { + return strings.Replace(p.ImportPath, "/", "-", -1) + "-" + strings.Replace(file, ".", "-", -1) + ".so" +} + +// swigDir returns the name of the shared SWIG directory for a +// package. +func (p *Package) swigDir(ctxt *build.Context) string { + dir := p.build.PkgRoot + if ctxt.Compiler == "gccgo" { + dir = filepath.Join(dir, "gccgo_"+ctxt.GOOS+"_"+ctxt.GOARCH) + } else { + dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH) + } + return filepath.Join(dir, "swig") +} + // packageList returns the list of packages in the dag rooted at roots // as visited in a depth-first post-order traversal. func packageList(roots []*Package) []*Package { @@ -459,7 +543,7 @@ func isStale(p *Package, topRoot map[string]bool) bool { // distributions of Go packages, although such binaries are // only useful with the specific version of the toolchain that // created them. - if len(p.gofiles) == 0 { + if len(p.gofiles) == 0 && !p.usesSwig() { return false } @@ -491,14 +575,19 @@ func isStale(p *Package, topRoot map[string]bool) bool { // As a courtesy to developers installing new versions of the compiler // frequently, define that packages are stale if they are // older than the compiler, and commands if they are older than - // the linker. This heuristic will not work if the binaries are back-dated, - // as some binary distributions may do, but it does handle a very - // common case. See issue 3036. - if olderThan(buildToolchain.compiler()) { - return true - } - if p.build.IsCommand() && olderThan(buildToolchain.linker()) { - return true + // the linker. This heuristic will not work if the binaries are + // back-dated, as some binary distributions may do, but it does handle + // a very common case. + // See issue 3036. + // Assume code in $GOROOT is up to date, since it may not be writeable. + // See issue 4106. + if p.Root != goroot { + if olderThan(buildToolchain.compiler()) { + return true + } + if p.build.IsCommand() && olderThan(buildToolchain.linker()) { + return true + } } // Have installed copy, probably built using current compilers, @@ -522,6 +611,21 @@ func isStale(p *Package, topRoot map[string]bool) bool { } } + for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) { + if olderThan(filepath.Join(p.Dir, src)) { + return true + } + soname := p.swigSoname(src) + fi, err := os.Stat(soname) + if err != nil { + return true + } + fiSrc, err := os.Stat(src) + if err != nil || fiSrc.ModTime().After(fi.ModTime()) { + return true + } + } + return false } @@ -546,13 +650,24 @@ func loadPackage(arg string, stk *importStack) *Package { arg = sub } } - if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { + if strings.HasPrefix(arg, "cmd/") { if p := cmdCache[arg]; p != nil { return p } stk.push(arg) defer stk.pop() - bp, err := build.ImportDir(filepath.Join(gorootSrc, arg), 0) + + if strings.Contains(arg[4:], "/") { + p := &Package{ + Error: &PackageError{ + ImportStack: stk.copy(), + Err: fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"), + }, + } + return p + } + + bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true bp.BinDir = gorootBin @@ -580,7 +695,7 @@ func loadPackage(arg string, stk *importStack) *Package { // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { - bp, _ := build.ImportDir(filepath.Join(cwd, arg), build.FindOnly) + bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } @@ -621,10 +736,14 @@ func packagesAndErrors(args []string) []*Package { args = importPaths(args) var pkgs []*Package var stk importStack + var set = make(map[string]bool) + for _, arg := range args { - pkgs = append(pkgs, loadPackage(arg, &stk)) + if !set[arg] { + pkgs = append(pkgs, loadPackage(arg, &stk)) + set[arg] = true + } } - computeStale(pkgs...) return pkgs diff --git a/src/cmd/go/run.go b/src/cmd/go/run.go index 6043b7e20..b50569363 100644 --- a/src/cmd/go/run.go +++ b/src/cmd/go/run.go @@ -34,6 +34,7 @@ func printStderr(args ...interface{}) (int, error) { } func runRun(cmd *Command, args []string) { + raceInit() var b builder b.init() b.print = printStderr @@ -45,6 +46,13 @@ func runRun(cmd *Command, args []string) { if len(files) == 0 { fatalf("go run: no go files listed") } + for _, file := range files { + if strings.HasSuffix(file, "_test.go") { + // goFilesPackage is going to assign this to TestGoFiles. + // Reject since it won't be part of the build. + fatalf("go run: cannot run *_test.go files (%s)", file) + } + } p := goFilesPackage(files) if p.Error != nil { fatalf("%s", p.Error) @@ -57,6 +65,21 @@ func runRun(cmd *Command, args []string) { fatalf("go run: cannot run non-main package") } p.target = "" // must build - not up to date + var src string + if len(p.GoFiles) > 0 { + src = p.GoFiles[0] + } else if len(p.CgoFiles) > 0 { + src = p.CgoFiles[0] + } else { + // this case could only happen if the provided source uses cgo + // while cgo is disabled. + hint := "" + if !buildContext.CgoEnabled { + hint = " (cgo is disabled)" + } + fatalf("go run: no suitable source files%s", hint) + } + p.exeName = src[:len(src)-len(".go")] // name temporary executable for first go file a1 := b.action(modeBuild, modeBuild, p) a := &action{f: (*builder).runProgram, args: cmdArgs, deps: []*action{a1}} b.do(a) @@ -83,6 +106,7 @@ func runStdin(cmdargs ...interface{}) { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + startSigHandlers() if err := cmd.Run(); err != nil { errorf("%v", err) } diff --git a/src/cmd/go/signal.go b/src/cmd/go/signal.go new file mode 100644 index 000000000..e8ba0d365 --- /dev/null +++ b/src/cmd/go/signal.go @@ -0,0 +1,31 @@ +// 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 main + +import ( + "os" + "os/signal" + "sync" +) + +// interrupted is closed, if go process is interrupted. +var interrupted = make(chan struct{}) + +// processSignals setups signal handler. +func processSignals() { + sig := make(chan os.Signal) + signal.Notify(sig, signalsToIgnore...) + go func() { + <-sig + close(interrupted) + }() +} + +var onceProcessSignals sync.Once + +// startSigHandlers start signal handlers. +func startSigHandlers() { + onceProcessSignals.Do(processSignals) +} diff --git a/src/cmd/go/signal_notunix.go b/src/cmd/go/signal_notunix.go new file mode 100644 index 000000000..ef13c1919 --- /dev/null +++ b/src/cmd/go/signal_notunix.go @@ -0,0 +1,13 @@ +// 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. + +// +build plan9 windows + +package main + +import ( + "os" +) + +var signalsToIgnore = []os.Signal{os.Interrupt} diff --git a/src/cmd/go/signal_unix.go b/src/cmd/go/signal_unix.go new file mode 100644 index 000000000..489a73b83 --- /dev/null +++ b/src/cmd/go/signal_unix.go @@ -0,0 +1,14 @@ +// 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. + +// +build darwin freebsd linux netbsd openbsd + +package main + +import ( + "os" + "syscall" +) + +var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index fe186d4bb..460061a11 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -76,6 +76,12 @@ if ! ./testgo test ./testdata/testimport; then ok=false fi +# Test installation with relative imports. +if ! ./testgo test -i ./testdata/testimport; then + echo "go test -i ./testdata/testimport failed" + ok=false +fi + # Test tests with relative imports in packages synthesized # from Go files named on the command line. if ! ./testgo test ./testdata/testimport/*.go; then @@ -119,6 +125,208 @@ elif ! test -x testdata/bin1/helloworld; then ok=false fi +# Reject relative paths in GOPATH. +if GOPATH=. ./testgo build testdata/src/go-cmd-test/helloworld.go; then + echo 'GOPATH="." go build should have failed, did not' + ok=false +fi + +if GOPATH=:$(pwd)/testdata:. ./testgo build go-cmd-test; then + echo 'GOPATH=":$(pwd)/testdata:." go build should have failed, did not' + ok=false +fi + +# issue 4104 +if [ $(./testgo test fmt fmt fmt fmt fmt | wc -l) -ne 1 ] ; then + echo 'go test fmt fmt fmt fmt fmt tested the same package multiple times' + ok=false +fi + +# ensure that output of 'go list' is consistent between runs +./testgo list std > test_std.list +if ! ./testgo list std | cmp -s test_std.list - ; then + echo "go list std ordering is inconsistent" + ok=false +fi +rm -f test_std.list + +# issue 4096. Validate the output of unsucessful go install foo/quxx +if [ $(./testgo install 'foo/quxx' 2>&1 | grep -c 'cannot find package "foo/quxx" in any of') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*cannot find package "foo/quxx" in any of' + ok=false +fi +# test GOROOT search failure is reported +if [ $(./testgo install 'foo/quxx' 2>&1 | egrep -c 'foo/quxx \(from \$GOROOT\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*foo/quxx (from $GOROOT)' + ok=false +fi +# test multiple GOPATH entries are reported separately +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/./src/foo/quxx') -ne 2 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)\n.*testdata/b/src/foo/quxx' + ok=false +fi +# test (from $GOPATH) annotation is reported for the first GOPATH entry +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/a/src/foo/quxx \(from \$GOPATH\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/a/src/foo/quxx (from $GOPATH)' + ok=false +fi +# but not on the second +if [ $(GOPATH=$(pwd)/testdata/a:$(pwd)/testdata/b ./testgo install 'foo/quxx' 2>&1 | egrep -c 'testdata/b/src/foo/quxx$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: .*testdata/b/src/foo/quxx' + ok=false +fi +# test missing GOPATH is reported +if [ $(GOPATH= ./testgo install 'foo/quxx' 2>&1 | egrep -c '\(\$GOPATH not set\)$') -ne 1 ] ; then + echo 'go install foo/quxx expected error: ($GOPATH not set)' + ok=false +fi + +# issue 4186. go get cannot be used to download packages to $GOROOT +# Test that without GOPATH set, go get should fail +d=$(mktemp -d -t testgo) +mkdir -p $d/src/pkg +if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then + echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset' + ok=false +fi +rm -rf $d +# Test that with GOPATH=$GOROOT, go get should fail +d=$(mktemp -d -t testgo) +mkdir -p $d/src/pkg +if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then + echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT' + ok=false +fi +rm -rf $d + +# issue 3941: args with spaces +d=$(mktemp -d -t testgo) +cat >$d/main.go<<EOF +package main +var extern string +func main() { + println(extern) +} +EOF +./testgo run -ldflags '-X main.extern "hello world"' $d/main.go 2>hello.out +if ! grep -q '^hello world' hello.out; then + echo "ldflags -X main.extern 'hello world' failed. Output:" + cat hello.out + ok=false +fi +rm -rf $d + +# test that go test -cpuprofile leaves binary behind +./testgo test -cpuprofile strings.prof strings || ok=false +if [ ! -x strings.test ]; then + echo "go test -cpuprofile did not create strings.test" + ok=false +fi +rm -f strings.prof strings.test + +# issue 4568. test that symlinks don't screw things up too badly. +old=$(pwd) +d=$(mktemp -d -t testgo) +mkdir -p $d/src +( + ln -s $d $d/src/dir1 + cd $d/src/dir1 + echo package p >p.go + export GOPATH=$d + if [ "$($old/testgo list -f '{{.Root}}' .)" != "$d" ]; then + echo got lost in symlink tree: + pwd + env|grep WD + $old/testgo list -json . dir1 + touch $d/failed + fi +) +if [ -f $d/failed ]; then + ok=false +fi +rm -rf $d + +# issue 4515. +d=$(mktemp -d -t testgo) +mkdir -p $d/src/example/a $d/src/example/b $d/bin +cat >$d/src/example/a/main.go <<EOF +package main +func main() {} +EOF +cat >$d/src/example/b/main.go <<EOF +// +build mytag + +package main +func main() {} +EOF +GOPATH=$d ./testgo install -tags mytag example/a example/b || ok=false +if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then + echo go install example/a example/b did not install binaries + ok=false +fi +rm -f $d/bin/* +GOPATH=$d ./testgo install -tags mytag example/... || ok=false +if [ ! -x $d/bin/a -o ! -x $d/bin/b ]; then + echo go install example/... did not install binaries + ok=false +fi +rm -f $d/bin/*go +export GOPATH=$d +if [ "$(./testgo list -tags mytag example/b...)" != "example/b" ]; then + echo go list example/b did not find example/b + ok=false +fi +unset GOPATH +rm -rf $d + +# issue 4773. case-insensitive collisions +d=$(mktemp -d -t testgo) +export GOPATH=$d +mkdir -p $d/src/example/a $d/src/example/b +cat >$d/src/example/a/a.go <<EOF +package p +import ( + _ "math/rand" + _ "math/Rand" +) +EOF +if ./testgo list example/a 2>$d/out; then + echo go list example/a should have failed, did not. + ok=false +elif ! grep "case-insensitive import collision" $d/out >/dev/null; then + echo go list example/a did not report import collision. + ok=false +fi +cat >$d/src/example/b/file.go <<EOF +package b +EOF +cat >$d/src/example/b/FILE.go <<EOF +package b +EOF +if [ $(ls $d/src/example/b | wc -l) = 2 ]; then + # case-sensitive file system, let directory read find both files + args="example/b" +else + # case-insensitive file system, list files explicitly on command line. + args="$d/src/example/b/file.go $d/src/example/b/FILE.go" +fi +if ./testgo list $args 2>$d/out; then + echo go list example/b should have failed, did not. + ok=false +elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then + echo go list example/b did not report file name collision. + ok=false +fi +unset GOPATH +rm -rf $d + +# Only succeeds if source order is preserved. +./testgo test testdata/example[12]_test.go + +# clean up +rm -rf testdata/bin testdata/bin1 +rm -f testgo + if $ok; then echo PASS else diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 870ab190f..3132ab210 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -48,6 +48,7 @@ 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. +Each listed package causes the execution of a separate test binary. By default, go test needs no arguments. It compiles and tests the package with source in the current directory, including tests, and runs the tests. @@ -80,28 +81,47 @@ var helpTestflag = &Command{ The 'go test' command takes both flags that apply to 'go test' itself and flags that apply to the resulting test binary. -The test binary, called pkg.test, where pkg is the name of the -directory containing the package sources, has its own flags: +The following flags are recognized by the 'go test' command and +control the execution of any test: - -test.v - Verbose output: log all tests as they are run. - - -test.run pattern - Run only those tests and examples matching the regular - expression. - - -test.bench pattern + -bench regexp Run benchmarks matching the regular expression. - By default, no benchmarks run. + By default, no benchmarks run. To run all benchmarks, + use '-bench .' or '-bench=.'. + + -benchmem + Print memory allocation statistics for benchmarks. + + -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). + + -blockprofile block.out + Write a goroutine blocking profile to the specified file + when all tests are complete. + + -blockprofilerate n + Control the detail provided in goroutine blocking profiles by setting + runtime.BlockProfileRate to n. See 'godoc runtime BlockProfileRate'. + 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. + + -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. - -test.cpuprofile cpu.out + -cpuprofile cpu.out Write a CPU profile to the specified file before exiting. - -test.memprofile mem.out + -memprofile mem.out Write a memory profile to the specified file when all tests are complete. - -test.memprofilerate n + -memprofilerate n Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate. See 'godoc runtime MemProfileRate'. To profile all memory allocations, use -test.memprofilerate=1 @@ -109,38 +129,46 @@ directory containing the package sources, has its own flags: garbage collector, provided the test can run in the available memory without garbage collection. - -test.parallel n + -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. - -test.short + -run regexp + Run only those tests and examples matching the regular + expression. + + -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. - -test.timeout t + -timeout t If a test runs longer than t, panic. - -test.benchtime n - Run enough iterations of each benchmark to take n seconds. - The default is 1 second. + -v + Verbose output: log all tests as they are run. - -test.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. +The test binary, called pkg.test where pkg is the name of the +directory containing the package sources, can be invoked directly +after building it with 'go test -c'. When invoking the test binary +directly, each of the standard flag names must be prefixed with 'test.', +as in -test.run=TestMyFunc or -test.v. -For convenience, each of these -test.X flags of the test binary is -also available as the flag -X in 'go test' itself. Flags not listed -here are passed through unaltered. For instance, the command +When running 'go test', flags not listed above are passed through +unaltered. For instance, the command go test -x -v -cpuprofile=prof.out -dir=testdata -update will compile the test binary and then run it as pkg.test -test.v -test.cpuprofile=prof.out -dir=testdata -update + +The test flags that generate profiles also leave the test binary in pkg.test +for use when analyzing the profiles. + +Flags not recognized by 'go test' must be placed after any specified packages. `, } @@ -160,8 +188,8 @@ A benchmark function is one named BenchmarkXXX and should have the signature, func BenchmarkXXX(b *testing.B) { ... } -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 and os.Stderr. +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. That output is compared against the function's "Output:" comment, which must be the last comment in the function body (see example below). An example with no such comment, or with no text after "Output:" is compiled @@ -191,6 +219,7 @@ See the documentation of the testing package for more information. var ( testC bool // -c flag + testProfile bool // some profiling flag testI bool // -i flag testV bool // -v flag testFiles []string // -file flag(s) TODO: not respected @@ -207,6 +236,7 @@ func runTest(cmd *Command, args []string) { var pkgArgs []string pkgArgs, testArgs = testFlags(args) + raceInit() pkgs := packagesForBuild(pkgArgs) if len(pkgs) == 0 { fatalf("no packages to test") @@ -215,12 +245,15 @@ func runTest(cmd *Command, args []string) { if testC && len(pkgs) != 1 { fatalf("cannot use -c flag with multiple packages") } + if testProfile && len(pkgs) != 1 { + fatalf("cannot use test profile flag with multiple packages") + } // If a test timeout was given and is parseable, set our kill timeout // 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 dt, err := time.ParseDuration(testTimeout); err == nil { + if dt, err := time.ParseDuration(testTimeout); err == nil && dt > 0 { testKillTimeout = dt + 1*time.Minute } @@ -276,7 +309,9 @@ func runTest(cmd *Command, args []string) { all := []string{} for path := range deps { - all = append(all, path) + if !build.IsLocalImport(path) { + all = append(all, path) + } } sort.Strings(all) @@ -361,7 +396,11 @@ func runTest(cmd *Command, args []string) { if args != "" { args = " " + args } - fmt.Fprintf(os.Stderr, "installing these packages with 'go test -i%s' will speed future tests.\n\n", args) + extraOpts := "" + if buildRace { + extraOpts = "-race " + } + fmt.Fprintf(os.Stderr, "installing these packages with 'go test %s-i%s' will speed future tests.\n\n", extraOpts, args) } b.do(root) @@ -405,7 +444,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, // Use last element of import path, not package name. // They differ when package name is "main". - _, elem := path.Split(p.ImportPath) + // But if the import path is "command-line-arguments", + // like it is during 'go run', use the package name. + var elem string + if p.ImportPath == "command-line-arguments" { + elem = p.Name + } else { + _, elem = path.Split(p.ImportPath) + } testBinary := elem + ".test" // The ptest package needs to be importable under the @@ -532,14 +578,17 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action, a.target = filepath.Join(testDir, testBinary) + exeSuffix pmainAction := a - if testC { - // -c flag: create action to copy binary to ./test.out. + if testC || testProfile { + // -c or profiling flag: create action to copy binary to ./test.out. runAction = &action{ f: (*builder).install, deps: []*action{pmainAction}, p: pmain, - target: testBinary + exeSuffix, + target: filepath.Join(cwd, testBinary+exeSuffix), } + pmainAction = runAction // in case we are running the test + } + if testC { printAction = &action{p: p, deps: []*action{runAction}} // nop } else { // run test @@ -586,6 +635,7 @@ func (b *builder) runTest(a *action) error { cmd := exec.Command(args[0], args[1:]...) cmd.Dir = a.p.Dir + cmd.Env = envForDir(cmd.Dir) var buf bytes.Buffer if testStreamOutput { cmd.Stdout = os.Stdout @@ -595,14 +645,34 @@ func (b *builder) runTest(a *action) error { cmd.Stderr = &buf } + // If there are any local SWIG dependencies, we want to load + // the shared library from the build directory. + if a.p.usesSwig() { + env := cmd.Env + found := false + prefix := "LD_LIBRARY_PATH=" + for i, v := range env { + if strings.HasPrefix(v, prefix) { + env[i] = v + ":." + found = true + break + } + } + if !found { + env = append(env, "LD_LIBRARY_PATH=.") + } + cmd.Env = env + } + t0 := time.Now() err := cmd.Start() // This is a last-ditch deadline to detect and // stop wedged test binaries, to keep the builders // running. - tick := time.NewTimer(testKillTimeout) if err == nil { + tick := time.NewTimer(testKillTimeout) + startSigHandlers() done := make(chan error) go func() { done <- cmd.Wait() @@ -613,13 +683,12 @@ func (b *builder) runTest(a *action) error { case <-tick.C: cmd.Process.Kill() err = <-done - fmt.Fprintf(&buf, "*** Test killed: ran too long.\n") + fmt.Fprintf(&buf, "*** Test killed: ran too long (%v).\n", testKillTimeout) } tick.Stop() } out := buf.Bytes() - t1 := time.Now() - t := fmt.Sprintf("%.3fs", t1.Sub(t0).Seconds()) + t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds()) if err == nil { if testShowPass { a.testOutput.Write(out) @@ -750,8 +819,10 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { *seen = true } } - for _, e := range doc.Examples(f) { - if e.Output == "" { + ex := doc.Examples(f) + sort.Sort(byOrder(ex)) + for _, e := range ex { + if e.Output == "" && !e.EmptyOutput { // Don't run examples with no output. continue } @@ -761,6 +832,12 @@ func (t *testFuncs) load(filename, pkg string, seen *bool) error { return nil } +type byOrder []*doc.Example + +func (x byOrder) Len() int { return len(x) } +func (x byOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byOrder) Less(i, j int) bool { return x[i].Order < x[j].Order } + var testmainTmpl = template.Must(template.New("main").Parse(` package main diff --git a/src/cmd/go/testdata/example1_test.go b/src/cmd/go/testdata/example1_test.go new file mode 100644 index 000000000..ec7092e97 --- /dev/null +++ b/src/cmd/go/testdata/example1_test.go @@ -0,0 +1,23 @@ +// 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. + +// Make sure that go test runs Example_Z before Example_A, preserving source order. + +package p + +import "fmt" + +var n int + +func Example_Z() { + n++ + fmt.Println(n) + // Output: 1 +} + +func Example_A() { + n++ + fmt.Println(n) + // Output: 2 +} diff --git a/src/cmd/go/testdata/example2_test.go b/src/cmd/go/testdata/example2_test.go new file mode 100644 index 000000000..1e0e80b80 --- /dev/null +++ b/src/cmd/go/testdata/example2_test.go @@ -0,0 +1,21 @@ +// 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. + +// Make sure that go test runs Example_Y before Example_B, preserving source order. + +package p + +import "fmt" + +func Example_Y() { + n++ + fmt.Println(n) + // Output: 3 +} + +func Example_B() { + n++ + fmt.Println(n) + // Output: 4 +} diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go index ecf5bf456..b2ca66b09 100644 --- a/src/cmd/go/testflag.go +++ b/src/cmd/go/testflag.go @@ -25,11 +25,14 @@ var usageMessage = `Usage of go test: // These flags can be passed with or without a "test." prefix: -v or -test.v. -bench="": passes -test.bench to test - -benchtime=1: passes -test.benchtime to test + -benchmem=false: print memory allocation statistics for benchmarks + -benchtime=1s: passes -test.benchtime to test -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test + -blockprofile="": pases -test.blockprofile to test + -blockprofilerate=0: passes -test.blockprofilerate to test -parallel=0: passes -test.parallel to test -run="": passes -test.run to test -short=false: passes -test.short to test @@ -71,14 +74,18 @@ var testFlagDefn = []*testFlagSpec{ {name: "gccgoflags"}, {name: "tags"}, {name: "compiler"}, + {name: "race", boolVar: &buildRace}, // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. {name: "bench", passToTest: true}, + {name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchtime", passToTest: true}, {name: "cpu", passToTest: true}, {name: "cpuprofile", passToTest: true}, {name: "memprofile", passToTest: true}, {name: "memprofilerate", passToTest: true}, + {name: "blockprofile", passToTest: true}, + {name: "blockprofilerate", passToTest: true}, {name: "parallel", passToTest: true}, {name: "run", passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true}, @@ -117,7 +124,7 @@ func testFlags(args []string) (packageNames, passToTest []string) { f, value, extraWord := testFlag(args, i) if f == nil { // This is a flag we do not know; we must assume - // that any args we see after this might be flag + // that any args we see after this might be flag // arguments, not package names. inPkg = false if packageNames == nil { @@ -127,18 +134,28 @@ func testFlags(args []string) (packageNames, passToTest []string) { passToTest = append(passToTest, args[i]) continue } + var err error switch f.name { // bool flags. - case "a", "c", "i", "n", "x", "v", "work": + case "a", "c", "i", "n", "x", "v", "work", "race": setBoolFlag(f.boolVar, value) case "p": setIntFlag(&buildP, value) case "gcflags": - buildGcflags = strings.Fields(value) + buildGcflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "ldflags": - buildLdflags = strings.Fields(value) + buildLdflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "gccgoflags": - buildGccgoflags = strings.Fields(value) + buildGccgoflags, err = splitQuotedFields(value) + if err != nil { + fatalf("invalid flag argument for -%s: %v", f.name, err) + } case "tags": buildContext.BuildTags = strings.Fields(value) case "compiler": @@ -150,6 +167,8 @@ func testFlags(args []string) (packageNames, passToTest []string) { testBench = true case "timeout": testTimeout = value + case "blockprofile", "cpuprofile", "memprofile": + testProfile = true } if extraWord { i++ @@ -176,9 +195,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) } name := arg[1:] // If there's already "test.", drop it for now. - if strings.HasPrefix(name, "test.") { - name = name[5:] - } + name = strings.TrimPrefix(name, "test.") equals := strings.Index(name, "=") if equals >= 0 { value = name[equals+1:] diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go index cb463a2e7..299b94cb3 100644 --- a/src/cmd/go/tool.go +++ b/src/cmd/go/tool.go @@ -47,7 +47,7 @@ const toolWindowsExtension = ".exe" func tool(name string) string { p := filepath.Join(toolDir, name) - if toolIsWindows { + if toolIsWindows && name != "pprof" { p += toolWindowsExtension } return p @@ -76,6 +76,16 @@ func runTool(cmd *Command, args []string) { setExitStatus(3) return } + if toolIsWindows && toolName == "pprof" { + args = append([]string{"perl", toolPath}, args[1:]...) + var err error + toolPath, err = exec.LookPath("perl") + if err != nil { + fmt.Fprintf(os.Stderr, "go tool: perl not found\n") + setExitStatus(3) + return + } + } if toolN { fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) @@ -90,7 +100,14 @@ func runTool(cmd *Command, args []string) { } err := toolCmd.Run() if err != nil { - fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) + // Only print about the exit status if the command + // didn't even run (not an ExitError) or it didn't exit cleanly + // or we're printing command lines too (-x mode). + // Assume if command exited cleanly (even with non-zero status) + // it printed any messages it wanted to print. + if e, ok := err.(*exec.ExitError); !ok || !e.Exited() || buildX { + fmt.Fprintf(os.Stderr, "go tool %s: %s\n", toolName, err) + } setExitStatus(1) return } diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 1c121672f..b99579441 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -81,7 +81,7 @@ var vcsHg = &vcsCmd{ tagSyncCmd: "update -r {tag}", tagSyncDefault: "update default", - scheme: []string{"https", "http"}, + scheme: []string{"https", "http", "ssh"}, pingCmd: "identify {scheme}://{repo}", } @@ -180,8 +180,17 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) args[i] = expand(m, arg) } + _, err := exec.LookPath(v.cmd) + if err != nil { + fmt.Fprintf(os.Stderr, + "go: missing %s command. See http://golang.org/s/gogetcmd\n", + v.name) + return nil, err + } + cmd := exec.Command(v.cmd, args...) cmd.Dir = dir + cmd.Env = envForDir(cmd.Dir) if buildX { fmt.Printf("cd %s\n", dir) fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " ")) @@ -189,7 +198,7 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) var buf bytes.Buffer cmd.Stdout = &buf cmd.Stderr = &buf - err := cmd.Run() + err = cmd.Run() out := buf.Bytes() if err != nil { if verbose || buildV { diff --git a/src/cmd/go/version.go b/src/cmd/go/version.go index 09e2f1633..a41f4a736 100644 --- a/src/cmd/go/version.go +++ b/src/cmd/go/version.go @@ -21,5 +21,5 @@ func runVersion(cmd *Command, args []string) { cmd.Usage() } - fmt.Printf("go version %s\n", runtime.Version()) + fmt.Printf("go version %s %s/%s\n", runtime.Version(), runtime.GOOS, runtime.GOARCH) } diff --git a/src/cmd/godoc/appinit.go b/src/cmd/godoc/appinit.go index 70da00110..996b2b850 100644 --- a/src/cmd/godoc/appinit.go +++ b/src/cmd/godoc/appinit.go @@ -17,9 +17,12 @@ import ( ) func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! w.WriteHeader(http.StatusNotFound) - servePage(w, relpath, "File "+relpath, "", "", contents) + servePage(w, Page{ + Title: "File " + relpath, + Subtitle: relpath, + Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! + }) } func init() { @@ -34,6 +37,7 @@ func init() { *indexFiles = indexFilenames *maxResults = 100 // reduce latency by limiting the number of fulltext search results *indexThrottle = 0.3 // in case *indexFiles is empty (and thus the indexer is run) + *showPlayground = true // read .zip file and set up file systems const zipfile = zipFilename @@ -48,6 +52,7 @@ func init() { readTemplates() initHandlers() registerPublicHandlers(http.DefaultServeMux) + registerPlaygroundHandlers(http.DefaultServeMux) // initialize default directory tree with corresponding timestamp. initFSTree() diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index f7f51d0a0..e68c0fa6b 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -68,8 +68,11 @@ func codewalk(w http.ResponseWriter, r *http.Request) { return } - b := applyTemplate(codewalkHTML, "codewalk", cw) - servePage(w, cw.Title, "Codewalk: "+cw.Title, "", "", b) + servePage(w, Page{ + Title: "Codewalk: " + cw.Title, + Tabtitle: cw.Title, + Body: applyTemplate(codewalkHTML, "codewalk", cw), + }) } // A Codewalk represents a single codewalk read from an XML file. @@ -199,8 +202,10 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string } } - b := applyTemplate(codewalkdirHTML, "codewalkdir", v) - servePage(w, "", "Codewalks", "", "", b) + servePage(w, Page{ + Title: "Codewalks", + Body: applyTemplate(codewalkdirHTML, "codewalkdir", v), + }) } // codewalkFileprint serves requests with ?fileprint=f&lo=lo&hi=hi. diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go index b9b529f87..fda7adce5 100644 --- a/src/cmd/godoc/dirtrees.go +++ b/src/cmd/godoc/dirtrees.go @@ -74,7 +74,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i // determine number of subdirectories and if there are package files ndirs := 0 hasPkgFiles := false - var synopses [4]string // prioritized package documentation (0 == highest priority) + var synopses [3]string // prioritized package documentation (0 == highest priority) for _, d := range list { switch { case isPkgDir(d): @@ -95,12 +95,10 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i switch file.Name.Name { case name: i = 0 // normal case: directory name matches package name - case fakePkgName: - i = 1 // synopses for commands case "main": - i = 2 // directory contains a main package + i = 1 // directory contains a main package default: - i = 3 // none of the above + i = 2 // none of the above } if 0 <= i && i < len(synopses) && synopses[i] == "" { synopses[i] = doc.Synopsis(file.Doc.Text()) @@ -229,9 +227,7 @@ func (dir *Directory) lookupLocal(name string) *Directory { } func splitPath(p string) []string { - if strings.HasPrefix(p, "/") { - p = p[1:] - } + p = strings.TrimPrefix(p, "/") if p == "" { return nil } @@ -264,7 +260,7 @@ type DirEntry struct { Height int // = DirList.MaxHeight - Depth, > 0 Path string // directory path; includes Name, relative to DirList root Name string // directory name - HasPkg bool // true if the directory contains at least one package + HasPkg bool // true if the directory contains at least one package Synopsis string // package documentation, if any } @@ -310,14 +306,9 @@ func (root *Directory) listing(skipRoot bool) *DirList { // the path is relative to root.Path - remove the root.Path // prefix (the prefix should always be present but avoid // crashes and check) - path := d.Path - if strings.HasPrefix(d.Path, root.Path) { - path = d.Path[len(root.Path):] - } + path := strings.TrimPrefix(d.Path, root.Path) // remove leading separator if any - path must be relative - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } + path = strings.TrimPrefix(path, "/") p.Path = path p.Name = d.Name p.HasPkg = d.HasPkg diff --git a/src/cmd/godoc/doc.go b/src/cmd/godoc/doc.go index 39ecc6e63..ddb6d2687 100644 --- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -67,8 +67,6 @@ The flags are: -maxresults=10000 maximum number of full text search results shown (no full text index is built if maxresults <= 0) - -path="" - additional package directories (colon-separated) -html print HTML in command-line mode -goroot=$GOROOT @@ -88,20 +86,8 @@ The flags are: zip file providing the file system to serve; disabled if empty By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set). -Additional directories may be specified via the -path flag which accepts a list -of colon-separated paths; unrooted paths are relative to the current working -directory. Each path is considered as an additional root for packages in order -of appearance. The last (absolute) path element is the prefix for the package -path. For instance, given the flag value: - - path=".:/home/bar:/public" - -for a godoc started in /home/user/godoc, absolute paths are mapped to package paths -as follows: - - /home/user/godoc/x -> godoc/x - /home/bar/x -> bar/x - /public/x -> public/x +This behavior can be altered by providing an alternative $GOROOT with the -goroot +flag. When godoc runs as a web server and -index is set, a search index is maintained. The index is created at startup. @@ -141,4 +127,4 @@ See "Godoc: documenting Go code" for how to write good comments for godoc: http://golang.org/doc/articles/godoc_documenting_go_code.html */ -package documentation +package main diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go index 09d7b2463..0309d7cab 100644 --- a/src/cmd/godoc/filesystem.go +++ b/src/cmd/godoc/filesystem.go @@ -41,7 +41,7 @@ import ( // paths can assume they are slash-separated and should be using // package path (often imported as pathpkg) to manipulate them, // even on Windows. -// +// var fs = nameSpace{} // the underlying file system for godoc // Setting debugNS = true will enable debugging prints about @@ -138,7 +138,7 @@ func hasPathPrefix(x, y string) bool { // but we want to be able to mount multiple file systems on a single // mount point and have the system behave as if the union of those // file systems were present at the mount point. -// For example, if the OS file system has a Go installation in +// For example, if the OS file system has a Go installation in // c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then // this name space creates the view we want for the godoc server: // @@ -179,7 +179,7 @@ func hasPathPrefix(x, y string) bool { // OS(`d:\Work1').ReadDir("/src/code") // OS(`d:\Work2').ReadDir("/src/code") // -// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by +// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by // just "/src" in the final two calls. // // OS is itself an implementation of a file system: it implements @@ -459,9 +459,7 @@ func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) { if hasPathPrefix(old, path) && old != path { // Find next element after path in old. elem := old[len(path):] - if strings.HasPrefix(elem, "/") { - elem = elem[1:] - } + elem = strings.TrimPrefix(elem, "/") if i := strings.Index(elem, "/"); i >= 0 { elem = elem[:i] } diff --git a/src/cmd/godoc/format.go b/src/cmd/godoc/format.go index 3b1b9a822..122ddc7d6 100644 --- a/src/cmd/godoc/format.go +++ b/src/cmd/godoc/format.go @@ -54,6 +54,8 @@ type SegmentWriter func(w io.Writer, text []byte, selections int) // Selection is ignored. // func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, sw SegmentWriter, selections ...Selection) { + // If we have a link writer, make the links + // selection the last entry in selections if lw != nil { selections = append(selections, links) } @@ -108,8 +110,8 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, break } // determine the kind of segment change - if index == len(selections)-1 { - // we have a link segment change: + if lw != nil && index == len(selections)-1 { + // we have a link segment change (see start of this function): // format the previous selection segment, write the // link tag and start a new selection segment segment(offs) @@ -119,7 +121,7 @@ func FormatSelections(w io.Writer, text []byte, lw LinkWriter, links Selection, } else { // we have a selection change: // format the previous selection segment, determine - // the new selection bitset and start a new segment + // the new selection bitset and start a new segment segment(offs) lastOffs = offs mask := 1 << uint(index) diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index f6dc678b4..872b0dc1e 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -12,8 +12,10 @@ import ( "go/ast" "go/build" "go/doc" + "go/format" "go/printer" "go/token" + htmlpkg "html" "io" "io/ioutil" "log" @@ -57,12 +59,13 @@ var ( // TODO(gri) consider the invariant that goroot always end in '/' goroot = flag.String("goroot", runtime.GOROOT(), "Go root directory") testDir = flag.String("testdir", "", "Go root subdirectory - for testing only (faster startups)") - pkgPath = flag.String("path", "", "additional package directories (colon-separated)") // layout control tabwidth = flag.Int("tabwidth", 4, "tab width") showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") templateDir = flag.String("templates", "", "directory containing alternate template files") + showPlayground = flag.Bool("play", false, "enable playground in web interface") + showExamples = flag.Bool("ex", false, "show examples in command line mode") // search index indexEnabled = flag.Bool("index", false, "enable search index") @@ -80,22 +83,19 @@ var ( fileServer http.Handler // default file server cmdHandler docServer pkgHandler docServer + + // which code 'Notes' to show + notes = flag.String("notes", "BUG", "comma separated list of Note markers as per pkg:go/doc") + // list of 'Notes' to show + notesToShow []string ) func initHandlers() { - // Add named directories in -path argument as - // subdirectories of src/pkg. - for _, p := range filepath.SplitList(*pkgPath) { - _, elem := filepath.Split(p) - if elem == "" { - log.Fatalf("invalid -path argument: %q has no final element", p) - } - fs.Bind("/src/pkg/"+elem, OS(p), "/", bindReplace) - } + notesToShow = strings.Split(*notes, ",") fileServer = http.FileServer(&httpFS{fs}) - cmdHandler = docServer{"/cmd/", "/src/cmd", false} - pkgHandler = docServer{"/pkg/", "/src/pkg", true} + cmdHandler = docServer{"/cmd/", "/src/cmd"} + pkgHandler = docServer{"/pkg/", "/src/pkg"} } func registerPublicHandlers(mux *http.ServeMux) { @@ -326,18 +326,62 @@ func startsWithUppercase(s string) bool { var exampleOutputRx = regexp.MustCompile(`(?i)//[[:space:]]*output:`) -func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { +// stripExampleSuffix strips lowercase braz in Foo_braz or Foo_Bar_braz from name +// while keeping uppercase Braz in Foo_Braz. +func stripExampleSuffix(name string) string { + if i := strings.LastIndex(name, "_"); i != -1 { + if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { + name = name[:i] + } + } + return name +} + +func example_textFunc(funcName string, examples []*doc.Example, fset *token.FileSet, indent string) string { + if !*showExamples { + return "" + } + var buf bytes.Buffer + first := true for _, eg := range examples { - name := eg.Name + name := stripExampleSuffix(eg.Name) + if name != funcName { + continue + } - // strip lowercase braz in Foo_braz or Foo_Bar_braz from name - // while keeping uppercase Braz in Foo_Braz - if i := strings.LastIndex(name, "_"); i != -1 { - if i < len(name)-1 && !startsWithUppercase(name[i+1:]) { - name = name[:i] - } + if !first { + buf.WriteString("\n") } + first = false + + // print code + cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} + var buf1 bytes.Buffer + writeNode(&buf1, fset, cnode) + code := buf1.String() + // Additional formatting if this is a function body. + if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { + // remove surrounding braces + code = code[1 : n-1] + // unindent + code = strings.Replace(code, "\n ", "\n", -1) + } + code = strings.Trim(code, "\n") + code = strings.Replace(code, "\n", "\n\t", -1) + + buf.WriteString(indent) + buf.WriteString("Example:\n\t") + buf.WriteString(code) + buf.WriteString("\n") + } + return buf.String() +} + +func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.FileSet) string { + var buf bytes.Buffer + for _, eg := range examples { + name := stripExampleSuffix(eg.Name) if name != funcName { continue @@ -347,9 +391,11 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File cnode := &printer.CommentedNode{Node: eg.Code, Comments: eg.Comments} code := node_htmlFunc(cnode, fset) out := eg.Output + wholeFile := true - // additional formatting if this is a function body + // Additional formatting if this is a function body. if n := len(code); n >= 2 && code[0] == '{' && code[n-1] == '}' { + wholeFile = false // remove surrounding braces code = code[1 : n-1] // unindent @@ -358,14 +404,28 @@ func example_htmlFunc(funcName string, examples []*doc.Example, fset *token.File if loc := exampleOutputRx.FindStringIndex(code); loc != nil { code = strings.TrimSpace(code[:loc[0]]) } - } else { - // drop output, as the output comment will appear in the code + } + + // Write out the playground code in standard Go style + // (use tabs, no comment highlight, etc). + play := "" + if eg.Play != nil && *showPlayground { + var buf bytes.Buffer + if err := format.Node(&buf, fset, eg.Play); err != nil { + log.Print(err) + } else { + play = buf.String() + } + } + + // Drop output, as the output comment will appear in the code. + if wholeFile && play == "" { out = "" } err := exampleHTML.Execute(&buf, struct { - Name, Doc, Code, Output string - }{eg.Name, eg.Doc, code, out}) + Name, Doc, Code, Play, Output string + }{eg.Name, eg.Doc, code, play, out}) if err != nil { log.Print(err) } @@ -393,6 +453,10 @@ func example_suffixFunc(name string) string { return suffix } +func noteTitle(note string) string { + return strings.Title(strings.ToLower(note)) +} + func splitExampleName(s string) (name, suffix string) { i := strings.LastIndex(s, "_") if 0 <= i && i < len(s)-1 && !startsWithUppercase(s[i+1:]) { @@ -408,9 +472,7 @@ func pkgLinkFunc(path string) string { relpath := path[1:] // because of the irregular mapping under goroot // we need to correct certain relative paths - if strings.HasPrefix(relpath, "src/pkg/") { - relpath = relpath[len("src/pkg/"):] - } + relpath = strings.TrimPrefix(relpath, "src/pkg/") return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL } @@ -485,8 +547,12 @@ var fmap = template.FuncMap{ // formatting of Examples "example_html": example_htmlFunc, + "example_text": example_textFunc, "example_name": example_nameFunc, "example_suffix": example_suffixFunc, + + // formatting of Notes + "noteTitle": noteTitle, } func readTemplate(name string) *template.Template { @@ -538,31 +604,28 @@ func readTemplates() { // ---------------------------------------------------------------------------- // Generic HTML wrapper -func servePage(w http.ResponseWriter, tabtitle, title, subtitle, query string, content []byte) { - if tabtitle == "" { - tabtitle = title - } - d := struct { - Tabtitle string - Title string - Subtitle string - SearchBox bool - Query string - Version string - Menu []byte - Content []byte - }{ - tabtitle, - title, - subtitle, - *indexEnabled, - query, - runtime.Version(), - nil, - content, - } - - if err := godocHTML.Execute(w, &d); err != nil { +// Page describes the contents of the top-level godoc webpage. +type Page struct { + Title string + Tabtitle string + Subtitle string + Query string + Body []byte + + // filled in by servePage + SearchBox bool + Playground bool + Version string +} + +func servePage(w http.ResponseWriter, page Page) { + if page.Tabtitle == "" { + page.Tabtitle = page.Title + } + page.SearchBox = *indexEnabled + page.Playground = *showPlayground + page.Version = runtime.Version() + if err := godocHTML.Execute(w, page); err != nil { log.Printf("godocHTML.Execute: %s", err) } } @@ -627,7 +690,11 @@ func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath strin src = buf.Bytes() } - servePage(w, "", meta.Title, meta.Subtitle, "", src) + servePage(w, Page{ + Title: meta.Title, + Subtitle: meta.Subtitle, + Body: src, + }) } func applyTemplate(t *template.Template, name string, data interface{}) []byte { @@ -640,11 +707,25 @@ func applyTemplate(t *template.Template, name string, data interface{}) []byte { func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { canonical := pathpkg.Clean(r.URL.Path) - if !strings.HasSuffix("/", canonical) { + if !strings.HasSuffix(canonical, "/") { canonical += "/" } if r.URL.Path != canonical { - http.Redirect(w, r, canonical, http.StatusMovedPermanently) + url := *r.URL + url.Path = canonical + http.Redirect(w, r, url.String(), http.StatusMovedPermanently) + redirected = true + } + return +} + +func redirectFile(w http.ResponseWriter, r *http.Request) (redirected bool) { + c := pathpkg.Clean(r.URL.Path) + c = strings.TrimRight(c, "/") + if r.URL.Path != c { + url := *r.URL + url.Path = c + http.Redirect(w, r, url.String(), http.StatusMovedPermanently) redirected = true } return @@ -658,12 +739,22 @@ func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, tit return } + if r.FormValue("m") == "text" { + serveText(w, src) + return + } + var buf bytes.Buffer buf.WriteString("<pre>") FormatText(&buf, src, 1, pathpkg.Ext(abspath) == ".go", r.FormValue("h"), rangeSelection(r.FormValue("s"))) buf.WriteString("</pre>") + fmt.Fprintf(&buf, `<p><a href="/%s?m=text">View as plain text</a></p>`, htmlpkg.EscapeString(relpath)) - servePage(w, relpath, title+" "+relpath, "", "", buf.Bytes()) + servePage(w, Page{ + Title: title + " " + relpath, + Tabtitle: relpath, + Body: buf.Bytes(), + }) } func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath string) { @@ -677,8 +768,11 @@ func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str return } - contents := applyTemplate(dirlistHTML, "dirlistHTML", list) - servePage(w, relpath, "Directory "+relpath, "", "", contents) + servePage(w, Page{ + Title: "Directory " + relpath, + Tabtitle: relpath, + Body: applyTemplate(dirlistHTML, "dirlistHTML", list), + }) } func serveFile(w http.ResponseWriter, r *http.Request) { @@ -734,6 +828,9 @@ func serveFile(w http.ResponseWriter, r *http.Request) { } if isTextFile(abspath) { + if redirectFile(w, r) { + return + } serveTextFile(w, r, abspath, relpath, "Text file") return } @@ -754,10 +851,6 @@ func serveSearchDesc(w http.ResponseWriter, r *http.Request) { // ---------------------------------------------------------------------------- // Packages -// Fake package file and name for commands. Contains the command documentation. -const fakePkgFile = "doc.go" -const fakePkgName = "documentation" - // Fake relative package path for built-ins. Documentation for all globals // (not just exported ones) will be shown for packages in this directory. const builtinPkgPath = "builtin" @@ -814,17 +907,21 @@ func remoteSearchURL(query string, html bool) string { } type PageInfo struct { - Dirname string // directory containing the package - PList []string // list of package names found - FSet *token.FileSet // corresponding file set - PAst *ast.File // nil if no single AST with package exports - PDoc *doc.Package // nil if no single package documentation - Examples []*doc.Example // nil if no example code - Dirs *DirList // nil if no directory information - DirTime time.Time // directory time stamp - DirFlat bool // if set, show directory in a flat (non-indented) manner - IsPkg bool // false if this is not documenting a real package - Err error // I/O error or nil + Dirname string // directory containing the package + Err error // error or nil + + // package info + FSet *token.FileSet // nil if no package documentation + PDoc *doc.Package // nil if no package documentation + Examples []*doc.Example // nil if no example code + Notes map[string][]string // nil if no package Notes + PAst *ast.File // nil if no AST with package exports + IsMain bool // true for package main + + // directory info + Dirs *DirList // nil if no directory information + DirTime time.Time // directory time stamp + DirFlat bool // if set, show directory in a flat (non-indented) manner } func (info *PageInfo) IsEmpty() bool { @@ -834,30 +931,97 @@ func (info *PageInfo) IsEmpty() bool { type docServer struct { pattern string // url pattern; e.g. "/pkg/" fsRoot string // file system root to which the pattern is mapped - isPkg bool // true if this handler serves real package documentation (as opposed to command documentation) } // fsReadDir implements ReadDir for the go/build package. func fsReadDir(dir string) ([]os.FileInfo, error) { - return fs.ReadDir(dir) + return fs.ReadDir(filepath.ToSlash(dir)) } // fsOpenFile implements OpenFile for the go/build package. func fsOpenFile(name string) (r io.ReadCloser, err error) { - data, err := ReadFile(fs, name) + data, err := ReadFile(fs, filepath.ToSlash(name)) if err != nil { return nil, err } return ioutil.NopCloser(bytes.NewReader(data)), nil } -func inList(name string, list []string) bool { - for _, l := range list { - if name == l { - return true +// packageExports is a local implementation of ast.PackageExports +// which correctly updates each package file's comment list. +// (The ast.PackageExports signature is frozen, hence the local +// implementation). +// +func packageExports(fset *token.FileSet, pkg *ast.Package) { + for _, src := range pkg.Files { + cmap := ast.NewCommentMap(fset, src, src.Comments) + ast.FileExports(src) + src.Comments = cmap.Filter(src).Comments() + } +} + +// addNames adds the names declared by decl to the names set. +// Method names are added in the form ReceiverTypeName_Method. +func addNames(names map[string]bool, decl ast.Decl) { + switch d := decl.(type) { + case *ast.FuncDecl: + name := d.Name.Name + if d.Recv != nil { + var typeName string + switch r := d.Recv.List[0].Type.(type) { + case *ast.StarExpr: + typeName = r.X.(*ast.Ident).Name + case *ast.Ident: + typeName = r.Name + } + name = typeName + "_" + name + } + names[name] = true + case *ast.GenDecl: + for _, spec := range d.Specs { + switch s := spec.(type) { + case *ast.TypeSpec: + names[s.Name.Name] = true + case *ast.ValueSpec: + for _, id := range s.Names { + names[id.Name] = true + } + } + } + } +} + +// globalNames returns a set of the names declared by all package-level +// declarations. Method names are returned in the form Receiver_Method. +func globalNames(pkg *ast.Package) map[string]bool { + names := make(map[string]bool) + for _, file := range pkg.Files { + for _, decl := range file.Decls { + addNames(names, decl) } } - return false + return names +} + +// collectExamples collects examples for pkg from testfiles. +func collectExamples(pkg *ast.Package, testfiles map[string]*ast.File) []*doc.Example { + var files []*ast.File + for _, f := range testfiles { + files = append(files, f) + } + + var examples []*doc.Example + globals := globalNames(pkg) + for _, e := range doc.Examples(files...) { + name := stripExampleSuffix(e.Name) + if name == "" || globals[name] { + examples = append(examples, e) + } else { + log.Printf("skipping example 'Example%s' because '%s' is not a known function or type", e.Name, e.Name) + } + } + + return examples } // getPageInfo returns the PageInfo for a package directory abspath. If the @@ -865,132 +1029,56 @@ func inList(name string, list []string) bool { // computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc) // is extracted from the AST. If there is no corresponding package in the // directory, PageInfo.PAst and PageInfo.PDoc are nil. If there are no sub- -// directories, PageInfo.Dirs is nil. If a directory read error occurred, -// PageInfo.Err is set to the respective error but the error is not logged. +// directories, PageInfo.Dirs is nil. If an error occurred, PageInfo.Err is +// set to the respective error but the error is not logged. // -func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { - var pkgFiles []string +func (h *docServer) getPageInfo(abspath, relpath string, mode PageInfoMode) (info PageInfo) { + info.Dirname = abspath - // If we're showing the default package, restrict to the ones - // that would be used when building the package on this - // system. This makes sure that if there are separate - // implementations for, say, Windows vs Unix, we don't + // Restrict to the package files that would be used when building + // the package on this system. This makes sure that if there are + // separate implementations for, say, Windows vs Unix, we don't // jumble them all together. - if pkgname == "" { - // Note: Uses current binary's GOOS/GOARCH. - // To use different pair, such as if we allowed the user - // to choose, set ctxt.GOOS and ctxt.GOARCH before - // calling ctxt.ScanDir. - ctxt := build.Default - ctxt.IsAbsPath = pathpkg.IsAbs - ctxt.ReadDir = fsReadDir - ctxt.OpenFile = fsOpenFile - dir, err := ctxt.ImportDir(abspath, 0) - if err == nil { - pkgFiles = append(dir.GoFiles, dir.CgoFiles...) - } - } - - // filter function to select the desired .go files - filter := func(d os.FileInfo) bool { - // Only Go files. - if !isPkgFile(d) { - return false - } - // If we are looking at cmd documentation, only accept - // the special fakePkgFile containing the documentation. - if !h.isPkg { - return d.Name() == fakePkgFile - } - // Also restrict file list to pkgFiles. - return pkgFiles == nil || inList(d.Name(), pkgFiles) - } - - // get package ASTs - fset := token.NewFileSet() - pkgs, err := parseDir(fset, abspath, filter) - if err != nil && pkgs == nil { - // only report directory read errors, ignore parse errors - // (may be able to extract partial package information) - return PageInfo{Dirname: abspath, Err: err} - } - - // select package - var pkg *ast.Package // selected package - var plist []string // list of other package (names), if any - if len(pkgs) == 1 { - // Exactly one package - select it. - for _, p := range pkgs { - pkg = p - } - - } else if len(pkgs) > 1 { - // Multiple packages - select the best matching package: The - // 1st choice is the package with pkgname, the 2nd choice is - // the package with dirname, and the 3rd choice is a package - // that is not called "main" if there is exactly one such - // package. Otherwise, don't select a package. - dirpath, dirname := pathpkg.Split(abspath) - - // If the dirname is "go" we might be in a sub-directory for - // .go files - use the outer directory name instead for better - // results. - if dirname == "go" { - _, dirname = pathpkg.Split(pathpkg.Clean(dirpath)) - } - - var choice3 *ast.Package - loop: - for _, p := range pkgs { - switch { - case p.Name == pkgname: - pkg = p - break loop // 1st choice; we are done - case p.Name == dirname: - pkg = p // 2nd choice - case p.Name != "main": - choice3 = p - } - } - if pkg == nil && len(pkgs) == 2 { - pkg = choice3 - } - - // Compute the list of other packages - // (excluding the selected package, if any). - plist = make([]string, len(pkgs)) - i := 0 - for name := range pkgs { - if pkg == nil || name != pkg.Name { - plist[i] = name - i++ - } - } - plist = plist[0:i] - sort.Strings(plist) + // Note: Uses current binary's GOOS/GOARCH. + // To use different pair, such as if we allowed the user to choose, + // set ctxt.GOOS and ctxt.GOARCH before calling ctxt.ImportDir. + ctxt := build.Default + ctxt.IsAbsPath = pathpkg.IsAbs + ctxt.ReadDir = fsReadDir + ctxt.OpenFile = fsOpenFile + pkginfo, err := ctxt.ImportDir(abspath, 0) + // continue if there are no Go source files; we still want the directory info + if _, nogo := err.(*build.NoGoError); err != nil && !nogo { + info.Err = err + return } - // get examples from *_test.go files - var examples []*doc.Example - filter = func(d os.FileInfo) bool { - return isGoFile(d) && strings.HasSuffix(d.Name(), "_test.go") - } - if testpkgs, err := parseDir(fset, abspath, filter); err != nil { - log.Println("parsing test files:", err) - } else { - for _, testpkg := range testpkgs { - var files []*ast.File - for _, f := range testpkg.Files { - files = append(files, f) - } - examples = append(examples, doc.Examples(files...)...) + // collect package files + pkgname := pkginfo.Name + pkgfiles := append(pkginfo.GoFiles, pkginfo.CgoFiles...) + if len(pkgfiles) == 0 { + // Commands written in C have no .go files in the build. + // Instead, documentation may be found in an ignored file. + // The file may be ignored via an explicit +build ignore + // constraint (recommended), or by defining the package + // documentation (historic). + pkgname = "main" // assume package main since pkginfo.Name == "" + pkgfiles = pkginfo.IgnoredGoFiles + } + + // get package information, if any + if len(pkgfiles) > 0 { + // build package AST + fset := token.NewFileSet() + files, err := parseFiles(fset, abspath, pkgfiles) + if err != nil { + info.Err = err + return } - } + pkg := &ast.Package{Name: pkgname, Files: files} - // compute package documentation - var past *ast.File - var pdoc *doc.Package - if pkg != nil { + // extract package documentation + info.FSet = fset if mode&showSource == 0 { // show extracted documentation var m doc.Mode @@ -1000,19 +1088,39 @@ func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM if mode&allMethods != 0 { m |= doc.AllMethods } - pdoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath + info.PDoc = doc.New(pkg, pathpkg.Clean(relpath), m) // no trailing '/' in importpath + + // collect examples + testfiles := append(pkginfo.TestGoFiles, pkginfo.XTestGoFiles...) + files, err = parseFiles(fset, abspath, testfiles) + if err != nil { + log.Println("parsing examples:", err) + } + info.Examples = collectExamples(pkg, files) + + // collect any notes that we want to show + if info.PDoc.Notes != nil { + info.Notes = make(map[string][]string) + for _, m := range notesToShow { + if n := info.PDoc.Notes[m]; n != nil { + info.Notes[m] = n + } + } + } + } else { // show source code // TODO(gri) Consider eliminating export filtering in this mode, // or perhaps eliminating the mode altogether. if mode&noFiltering == 0 { - ast.PackageExports(pkg) + packageExports(fset, pkg) } - past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments) + info.PAst = ast.MergePackageFiles(pkg, 0) } + info.IsMain = pkgname == "main" } - // get directory information + // get directory information, if any var dir *Directory var timestamp time.Time if tree, ts := fsTree.get(); tree != nil && tree.(*Directory) != nil { @@ -1030,20 +1138,11 @@ func (h *docServer) getPageInfo(abspath, relpath, pkgname string, mode PageInfoM dir = newDirectory(abspath, 1) timestamp = time.Now() } + info.Dirs = dir.listing(true) + info.DirTime = timestamp + info.DirFlat = mode&flatDir != 0 - return PageInfo{ - Dirname: abspath, - PList: plist, - FSet: fset, - PAst: past, - PDoc: pdoc, - Examples: examples, - Dirs: dir.listing(true), - DirTime: timestamp, - DirFlat: mode&flatDir != 0, - IsPkg: h.isPkg, - Err: nil, - } + return } func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -1057,7 +1156,7 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { if relpath == builtinPkgPath { mode = noFiltering } - info := h.getPageInfo(abspath, relpath, r.FormValue("p"), mode) + info := h.getPageInfo(abspath, relpath, mode) if info.Err != nil { log.Print(info.Err) serveError(w, r, relpath, info.Err) @@ -1065,8 +1164,7 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { } if mode&noHtml != 0 { - contents := applyTemplate(packageText, "packageText", info) - serveText(w, contents) + serveText(w, applyTemplate(packageText, "packageText", info)) return } @@ -1074,26 +1172,25 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case info.PAst != nil: tabtitle = info.PAst.Name.Name - title = "Package " + tabtitle case info.PDoc != nil: - if info.PDoc.Name == fakePkgName { - // assume that the directory name is the command name - _, tabtitle = pathpkg.Split(relpath) - } else { - tabtitle = info.PDoc.Name - } - if info.IsPkg { - title = "Package " + tabtitle - } else { - title = "Command " + tabtitle - } + tabtitle = info.PDoc.Name default: tabtitle = info.Dirname - title = "Directory " + tabtitle + title = "Directory " if *showTimestamps { subtitle = "Last update: " + info.DirTime.String() } } + if title == "" { + if info.IsMain { + // assume that the directory name is the command name + _, tabtitle = pathpkg.Split(relpath) + title = "Command " + } else { + title = "Package " + } + } + title += tabtitle // special cases for top-level package/command directories switch tabtitle { @@ -1103,8 +1200,12 @@ func (h *docServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { tabtitle = "Commands" } - contents := applyTemplate(packageHTML, "packageHTML", info) - servePage(w, tabtitle, title, subtitle, "", contents) + servePage(w, Page{ + Title: title, + Tabtitle: tabtitle, + Subtitle: subtitle, + Body: applyTemplate(packageHTML, "packageHTML", info), + }) } // ---------------------------------------------------------------------------- @@ -1181,8 +1282,7 @@ func search(w http.ResponseWriter, r *http.Request) { result := lookup(query) if getPageInfoMode(r)&noHtml != 0 { - contents := applyTemplate(searchText, "searchText", result) - serveText(w, contents) + serveText(w, applyTemplate(searchText, "searchText", result)) return } @@ -1193,8 +1293,12 @@ func search(w http.ResponseWriter, r *http.Request) { title = fmt.Sprintf(`No results found for query %q`, query) } - contents := applyTemplate(searchHTML, "searchHTML", result) - servePage(w, query, title, "", query, contents) + servePage(w, Page{ + Title: title, + Tabtitle: query, + Query: query, + Body: applyTemplate(searchHTML, "searchHTML", result), + }) } // ---------------------------------------------------------------------------- diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 1bef79693..91c56461a 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -148,7 +148,7 @@ func init() { // sanity check: if nKinds is too large, the SpotInfo // accessor functions may need to be updated if nKinds > 8 { - panic("nKinds > 8") + panic("internal error: nKinds > 8") } } @@ -457,12 +457,6 @@ func (x *Indexer) addSnippet(s *Snippet) int { return index } -func (x *Indexer) visitComment(c *ast.CommentGroup) { - if c != nil { - ast.Walk(x, c) - } -} - func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { lists, found := x.words[id.Name] @@ -486,20 +480,24 @@ func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { } } -func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { +func (x *Indexer) visitFieldList(kind SpotKind, list *ast.FieldList) { + for _, f := range list.List { + x.decl = nil // no snippets for fields + for _, name := range f.Names { + x.visitIdent(kind, name) + } + ast.Walk(x, f.Type) + // ignore tag - not indexed at the moment + } +} + +func (x *Indexer) visitSpec(kind SpotKind, spec ast.Spec) { switch n := spec.(type) { case *ast.ImportSpec: - x.visitComment(n.Doc) x.visitIdent(ImportDecl, n.Name) - ast.Walk(x, n.Path) - x.visitComment(n.Comment) + // ignore path - not indexed at the moment case *ast.ValueSpec: - x.visitComment(n.Doc) - kind := ConstDecl - if isVarDecl { - kind = VarDecl - } for _, n := range n.Names { x.visitIdent(kind, n) } @@ -507,57 +505,51 @@ func (x *Indexer) visitSpec(spec ast.Spec, isVarDecl bool) { for _, v := range n.Values { ast.Walk(x, v) } - x.visitComment(n.Comment) case *ast.TypeSpec: - x.visitComment(n.Doc) x.visitIdent(TypeDecl, n.Name) ast.Walk(x, n.Type) - x.visitComment(n.Comment) + } +} + +func (x *Indexer) visitGenDecl(decl *ast.GenDecl) { + kind := VarDecl + if decl.Tok == token.CONST { + kind = ConstDecl + } + x.decl = decl + for _, s := range decl.Specs { + x.visitSpec(kind, s) } } func (x *Indexer) Visit(node ast.Node) ast.Visitor { - // TODO(gri): methods in interface types are categorized as VarDecl switch n := node.(type) { case nil: - return nil + // nothing to do case *ast.Ident: x.visitIdent(Use, n) - case *ast.Field: - x.decl = nil // no snippets for fields - x.visitComment(n.Doc) - for _, m := range n.Names { - x.visitIdent(VarDecl, m) - } - ast.Walk(x, n.Type) - ast.Walk(x, n.Tag) - x.visitComment(n.Comment) + case *ast.FieldList: + x.visitFieldList(VarDecl, n) + + case *ast.InterfaceType: + x.visitFieldList(MethodDecl, n.Methods) case *ast.DeclStmt: + // local declarations should only be *ast.GenDecls; + // ignore incorrect ASTs if decl, ok := n.Decl.(*ast.GenDecl); ok { - // local declarations can only be *ast.GenDecls x.decl = nil // no snippets for local declarations - x.visitComment(decl.Doc) - for _, s := range decl.Specs { - x.visitSpec(s, decl.Tok == token.VAR) - } - } else { - // handle error case gracefully - ast.Walk(x, n.Decl) + x.visitGenDecl(decl) } case *ast.GenDecl: x.decl = n - x.visitComment(n.Doc) - for _, s := range n.Specs { - x.visitSpec(s, n.Tok == token.VAR) - } + x.visitGenDecl(n) case *ast.FuncDecl: - x.visitComment(n.Doc) kind := FuncDecl if n.Recv != nil { kind = MethodDecl @@ -571,15 +563,11 @@ func (x *Indexer) Visit(node ast.Node) ast.Visitor { } case *ast.File: - x.visitComment(n.Doc) x.decl = nil x.visitIdent(PackageClause, n.Name) for _, d := range n.Decls { ast.Walk(x, d) } - // don't visit package level comments for now - // to avoid duplicate visiting from individual - // nodes default: return x @@ -622,7 +610,7 @@ func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast * // the file set implementation changed or we have another error. base := x.fset.Base() if x.sources.Len() != base { - panic("internal error - file base incorrect") + panic("internal error: file base incorrect") } // append file contents (src) to x.sources diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 2e2889ed3..134410090 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -36,6 +36,7 @@ import ( "fmt" "go/ast" "go/build" + "go/printer" "io" "log" "net/http" @@ -73,9 +74,12 @@ var ( ) func serveError(w http.ResponseWriter, r *http.Request, relpath string, err error) { - contents := applyTemplate(errorHTML, "errorHTML", err) // err may contain an absolute path! w.WriteHeader(http.StatusNotFound) - servePage(w, relpath, "File "+relpath, "", "", contents) + servePage(w, Page{ + Title: "File " + relpath, + Subtitle: relpath, + Body: applyTemplate(errorHTML, "errorHTML", err), // err may contain an absolute path! + }) } func usage() { @@ -221,6 +225,8 @@ func main() { // Print content that would be served at the URL *urlFlag. if *urlFlag != "" { registerPublicHandlers(http.DefaultServeMux) + initFSTree() + updateMetadata() // Try up to 10 fetches, following redirects. urlstr := *urlFlag for i := 0; i < 10; i++ { @@ -278,10 +284,7 @@ func main() { } registerPublicHandlers(http.DefaultServeMux) - - // Playground handlers are not available in local godoc. - http.HandleFunc("/compile", disabledHandler) - http.HandleFunc("/share", disabledHandler) + registerPlaygroundHandlers(http.DefaultServeMux) // Initialize default directory tree with corresponding timestamp. // (Do it in a goroutine so that launch is quick.) @@ -344,7 +347,7 @@ func main() { fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if strings.HasPrefix(path, cmdPrefix) { - path = path[len(cmdPrefix):] + path = strings.TrimPrefix(path, cmdPrefix) forceCmd = true } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, OS(bp.Dir), "/", bindReplace) @@ -369,13 +372,11 @@ func main() { } mode |= showSource } - // TODO(gri): Provide a mechanism (flag?) to select a package - // if there are multiple packages in a directory. // first, try as package unless forced as command var info PageInfo if !forceCmd { - info = pkgHandler.getPageInfo(abspath, relpath, "", mode) + info = pkgHandler.getPageInfo(abspath, relpath, mode) } // second, try as command unless the path is absolute @@ -383,7 +384,7 @@ func main() { var cinfo PageInfo if !filepath.IsAbs(path) { abspath = pathpkg.Join(cmdHandler.fsRoot, path) - cinfo = cmdHandler.getPageInfo(abspath, relpath, "", mode) + cinfo = cmdHandler.getPageInfo(abspath, relpath, mode) } // determine what to use @@ -421,20 +422,24 @@ func main() { filter := func(s string) bool { return rx.MatchString(s) } switch { case info.PAst != nil: + cmap := ast.NewCommentMap(info.FSet, info.PAst, info.PAst.Comments) ast.FilterFile(info.PAst, filter) // Special case: Don't use templates for printing // so we only get the filtered declarations without // package clause or extra whitespace. for i, d := range info.PAst.Decls { + // determine the comments associated with d only + comments := cmap.Filter(d).Comments() + cn := &printer.CommentedNode{Node: d, Comments: comments} if i > 0 { fmt.Println() } if *html { var buf bytes.Buffer - writeNode(&buf, info.FSet, d) + writeNode(&buf, info.FSet, cn) FormatText(os.Stdout, buf.Bytes(), -1, true, "", nil) } else { - writeNode(os.Stdout, info.FSet, d) + writeNode(os.Stdout, info.FSet, cn) } fmt.Println() } @@ -459,9 +464,3 @@ type httpWriter struct { func (w *httpWriter) Header() http.Header { return w.h } func (w *httpWriter) WriteHeader(code int) { w.code = code } - -// disabledHandler serves a 501 "Not Implemented" response. -func disabledHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) - fmt.Fprint(w, "This functionality is not available via local godoc.") -} diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go index c6b7c2dc8..42a5d2d98 100644 --- a/src/cmd/godoc/parser.go +++ b/src/cmd/godoc/parser.go @@ -2,10 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file contains support functions for parsing .go files. -// Similar functionality is found in package go/parser but the -// functions here operate using godoc's file system fs instead -// of calling the OS's file operations directly. +// This file contains support functions for parsing .go files +// accessed via godoc's file system fs. package main @@ -13,7 +11,6 @@ import ( "go/ast" "go/parser" "go/token" - "os" pathpkg "path" ) @@ -25,44 +22,16 @@ func parseFile(fset *token.FileSet, filename string, mode parser.Mode) (*ast.Fil return parser.ParseFile(fset, filename, src, mode) } -func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first error) { - pkgs = make(map[string]*ast.Package) - for _, filename := range filenames { - file, err := parseFile(fset, filename, parser.ParseComments) +func parseFiles(fset *token.FileSet, abspath string, localnames []string) (map[string]*ast.File, error) { + files := make(map[string]*ast.File) + for _, f := range localnames { + absname := pathpkg.Join(abspath, f) + file, err := parseFile(fset, absname, parser.ParseComments) if err != nil { - if first == nil { - first = err - } - continue - } - - name := file.Name.Name - pkg, found := pkgs[name] - if !found { - // TODO(gri) Use NewPackage here; reconsider ParseFiles API. - pkg = &ast.Package{Name: name, Files: make(map[string]*ast.File)} - pkgs[name] = pkg - } - pkg.Files[filename] = file - } - return -} - -func parseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool) (map[string]*ast.Package, error) { - list, err := fs.ReadDir(path) - if err != nil { - return nil, err - } - - filenames := make([]string, len(list)) - i := 0 - for _, d := range list { - if filter == nil || filter(d) { - filenames[i] = pathpkg.Join(path, d.Name()) - i++ + return nil, err } + files[absname] = file } - filenames = filenames[0:i] - return parseFiles(fset, filenames) + return files, nil } diff --git a/src/cmd/godoc/play-appengine.go b/src/cmd/godoc/play-appengine.go new file mode 100644 index 000000000..9e351d1a2 --- /dev/null +++ b/src/cmd/godoc/play-appengine.go @@ -0,0 +1,35 @@ +// 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. + +// App Engine godoc Playground functionality. + +// +build appengine + +package main + +import ( + "io" + "net/http" + + "appengine" + "appengine/urlfetch" +) + +func bounceToPlayground(w http.ResponseWriter, req *http.Request) { + c := appengine.NewContext(req) + client := urlfetch.Client(c) + url := playgroundBaseURL + req.URL.Path + defer req.Body.Close() + resp, err := client.Post(url, req.Header.Get("Content-type"), req.Body) + if err != nil { + http.Error(w, "Internal Server Error", 500) + c.Errorf("making POST request: %v", err) + return + } + defer resp.Body.Close() + if _, err := io.Copy(w, resp.Body); err != nil { + http.Error(w, "Internal Server Error", 500) + c.Errorf("making POST request: %v", err) + } +} diff --git a/src/cmd/godoc/play-local.go b/src/cmd/godoc/play-local.go new file mode 100644 index 000000000..637ce5e1a --- /dev/null +++ b/src/cmd/godoc/play-local.go @@ -0,0 +1,41 @@ +// 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. + +// Stand-alone godoc Playground functionality. + +// +build !appengine + +package main + +import ( + "io" + "net/http" + "net/url" +) + +var playgroundScheme, playgroundHost string + +func init() { + u, err := url.Parse(playgroundBaseURL) + if err != nil { + panic(err) + } + playgroundScheme = u.Scheme + playgroundHost = u.Host +} + +// bounceToPlayground forwards the request to play.golang.org. +func bounceToPlayground(w http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + req.URL.Scheme = playgroundScheme + req.URL.Host = playgroundHost + resp, err := http.Post(req.URL.String(), req.Header.Get("Content-type"), req.Body) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + w.WriteHeader(resp.StatusCode) + io.Copy(w, resp.Body) + resp.Body.Close() +} diff --git a/src/cmd/godoc/play.go b/src/cmd/godoc/play.go new file mode 100644 index 000000000..47a11f6c0 --- /dev/null +++ b/src/cmd/godoc/play.go @@ -0,0 +1,52 @@ +// 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. + +// Common Playground functionality. + +package main + +import ( + "encoding/json" + "fmt" + "go/format" + "net/http" +) + +// The server that will service compile and share requests. +const playgroundBaseURL = "http://play.golang.org" + +func registerPlaygroundHandlers(mux *http.ServeMux) { + if *showPlayground { + mux.HandleFunc("/compile", bounceToPlayground) + mux.HandleFunc("/share", bounceToPlayground) + } else { + mux.HandleFunc("/compile", disabledHandler) + mux.HandleFunc("/share", disabledHandler) + } + http.HandleFunc("/fmt", fmtHandler) +} + +type fmtResponse struct { + Body string + Error string +} + +// fmtHandler takes a Go program in its "body" form value, formats it with +// standard gofmt formatting, and writes a fmtResponse as a JSON object. +func fmtHandler(w http.ResponseWriter, r *http.Request) { + resp := new(fmtResponse) + body, err := format.Source([]byte(r.FormValue("body"))) + if err != nil { + resp.Error = err.Error() + } else { + resp.Body = string(body) + } + json.NewEncoder(w).Encode(resp) +} + +// disabledHandler serves a 501 "Not Implemented" response. +func disabledHandler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) + fmt.Fprint(w, "This functionality is not available via local godoc.") +} diff --git a/src/cmd/godoc/setup-godoc-app.bash b/src/cmd/godoc/setup-godoc-app.bash index b8dc4dcf9..792e0d450 100644..100755 --- a/src/cmd/godoc/setup-godoc-app.bash +++ b/src/cmd/godoc/setup-godoc-app.bash @@ -4,13 +4,14 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -# This script creates the .zip, index, and configuration files for running -# godoc on app-engine. +# This script creates a complete godoc app in $APPDIR. +# It copies the cmd/godoc and src/pkg/go/... sources from GOROOT, +# synthesizes an app.yaml file, and creates the .zip, index, and +# configuration files. # # If an argument is provided it is assumed to be the app-engine godoc directory. -# Without an argument, $APPDIR is used instead. If GOROOT is not set, the -# current working directory is assumed to be $GOROOT. Various sanity checks -# prevent accidents. +# Without an argument, $APPDIR is used instead. If GOROOT is not set, "go env" +# is consulted to find the $GOROOT. # # The script creates a .zip file representing the $GOROOT file system # and computes the correspondig search index files. These files are then @@ -29,8 +30,8 @@ error() { getArgs() { if [ -z $GOROOT ]; then - GOROOT=$(pwd) - echo "GOROOT not set, using cwd instead" + GOROOT=$(go env GOROOT) + echo "GOROOT not set explicitly, using $GOROOT instead" fi if [ -z $APPDIR ]; then if [ $# == 0 ]; then @@ -47,14 +48,8 @@ getArgs() { if [ ! -x $GOROOT/bin/godoc ]; then error "$GOROOT/bin/godoc does not exist or is not executable" fi - if [ ! -d $APPDIR ]; then - error "$APPDIR is not a directory" - fi - if [ ! -e $APPDIR/app.yaml ]; then - error "$APPDIR is not an app-engine directory; missing file app.yaml" - fi - if [ ! -d $APPDIR/godoc ]; then - error "$APPDIR is missing directory godoc" + if [ -e $APPDIR ]; then + error "$APPDIR exists; check and remove it before trying again" fi # reporting @@ -62,12 +57,32 @@ getArgs() { echo "APPDIR = $APPDIR" } -cleanup() { - echo "*** cleanup $APPDIR" - rm $APPDIR/$ZIPFILE - rm $APPDIR/$INDEXFILE - rm $APPDIR/$SPLITFILES* - rm $APPDIR/$CONFIGFILE +copyGodoc() { + echo "*** copy $GOROOT/src/cmd/godoc to $APPDIR/godoc" + cp -r $GOROOT/src/cmd/godoc $APPDIR/godoc +} + +copyGoPackages() { + echo "*** copy $GOROOT/src/pkg/go to $APPDIR/newgo and rewrite imports" + cp -r $GOROOT/src/pkg/go $APPDIR/newgo + find $APPDIR/newgo -type d -name testdata | xargs rm -r + gofiles=$(find $APPDIR -name '*.go') + sed -i '' 's_^\(."\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles + sed -i '' 's_^\(import "\)\(go/[a-z]*\)"$_\1new\2"_' $gofiles +} + +makeAppYaml() { + echo "*** make $APPDIR/app.yaml" + cat > $APPDIR/app.yaml <<EOF +application: godoc +version: 1 +runtime: go +api_version: go1 + +handlers: +- url: /.* + script: _go_app +EOF } makeZipfile() { @@ -112,7 +127,11 @@ EOF } getArgs "$@" -cleanup +set -e +mkdir $APPDIR +copyGodoc +copyGoPackages +makeAppYaml makeZipfile makeIndexfile splitIndexfile diff --git a/src/cmd/godoc/template.go b/src/cmd/godoc/template.go index d709baef4..7b9b9cfeb 100644 --- a/src/cmd/godoc/template.go +++ b/src/cmd/godoc/template.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Template support for writing HTML documents. -// Documents that include Template: true in their +// Documents that include Template: true in their // metadata are executed as input to text/template. // // This file defines functions for those templates to invoke. @@ -57,8 +57,8 @@ func contents(name string) string { return string(file) } -// format returns a textual representation of the arg, formatted according to its nature. -func format(arg interface{}) string { +// stringFor returns a textual representation of the arg, formatted according to its nature. +func stringFor(arg interface{}) string { switch arg := arg.(type) { case int: return fmt.Sprintf("%d", arg) @@ -87,10 +87,10 @@ func code(file string, arg ...interface{}) (s string, err error) { // text is already whole file. command = fmt.Sprintf("code %q", file) case 1: - command = fmt.Sprintf("code %q %s", file, format(arg[0])) + command = fmt.Sprintf("code %q %s", file, stringFor(arg[0])) text = oneLine(file, text, arg[0]) case 2: - command = fmt.Sprintf("code %q %s %s", file, format(arg[0]), format(arg[1])) + command = fmt.Sprintf("code %q %s %s", file, stringFor(arg[0]), stringFor(arg[1])) text = multipleLines(file, text, arg[0], arg[1]) default: return "", fmt.Errorf("incorrect code invocation: code %q %q", file, arg) diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index 65842a3b1..fffc7f06e 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -72,6 +72,6 @@ To convert the package tree from explicit slice upper bounds to implicit ones: gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg */ -package documentation +package main // BUG(rsc): The implementation of -r is a bit slow. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 0bc385b5b..861ff9390 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -29,7 +29,7 @@ var ( rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')") simplifyAST = flag.Bool("s", false, "simplify code") doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") - allErrors = flag.Bool("e", false, "print all (including spurious) errors") + allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)") // layout control comments = flag.Bool("comments", true, "print comments") @@ -65,7 +65,7 @@ func initParserMode() { parserMode |= parser.ParseComments } if *allErrors { - parserMode |= parser.SpuriousErrors + parserMode |= parser.AllErrors } } diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go index 4b2805009..202d0a50c 100644 --- a/src/cmd/gofmt/gofmt_test.go +++ b/src/cmd/gofmt/gofmt_test.go @@ -56,31 +56,37 @@ func runTest(t *testing.T, in, out, flags string) { return } - if got := buf.Bytes(); bytes.Compare(got, expected) != 0 { + if got := buf.Bytes(); !bytes.Equal(got, expected) { t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) d, err := diff(expected, got) if err == nil { t.Errorf("%s", d) } - ioutil.WriteFile(in+".gofmt", got, 0666) + if err := ioutil.WriteFile(in+".gofmt", got, 0666); err != nil { + t.Error(err) + } } } -// TODO(gri) Add more test cases! var tests = []struct { in, flags string }{ {"gofmt.go", ""}, {"gofmt_test.go", ""}, {"testdata/composites.input", "-s"}, + {"testdata/slices1.input", "-s"}, + {"testdata/slices2.input", "-s"}, {"testdata/old.input", ""}, {"testdata/rewrite1.input", "-r=Foo->Bar"}, {"testdata/rewrite2.input", "-r=int->bool"}, {"testdata/rewrite3.input", "-r=x->x"}, {"testdata/rewrite4.input", "-r=(x)->x"}, + {"testdata/rewrite5.input", "-r=x+x->2*x"}, {"testdata/stdin*.input", "-stdin"}, {"testdata/comments.input", ""}, {"testdata/import.input", ""}, + {"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF + {"testdata/typeswitch.input", ""}, // test case for issue 4470 } func TestRewrite(t *testing.T) { @@ -103,3 +109,24 @@ func TestRewrite(t *testing.T) { } } } + +func TestCRLF(t *testing.T) { + const input = "testdata/crlf.input" // must contain CR/LF's + const golden = "testdata/crlf.golden" // must not contain any CR's + + data, err := ioutil.ReadFile(input) + if err != nil { + t.Error(err) + } + if bytes.Index(data, []byte("\r\n")) < 0 { + t.Errorf("%s contains no CR/LF's", input) + } + + data, err = ioutil.ReadFile(golden) + if err != nil { + t.Error(err) + } + if bytes.Index(data, []byte("\r")) >= 0 { + t.Errorf("%s contains CR's", golden) + } +} diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go index edbce606a..862e9d987 100644 --- a/src/cmd/gofmt/long_test.go +++ b/src/cmd/gofmt/long_test.go @@ -84,7 +84,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { } // the first and 2nd result should be identical - if bytes.Compare(b1.Bytes(), b2.Bytes()) != 0 { + if !bytes.Equal(b1.Bytes(), b2.Bytes()) { t.Errorf("gofmt %s not idempotent", filename) } } diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index 3c7861f0d..dfabb6198 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -55,6 +55,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 { + cmap := ast.NewCommentMap(fileSet, p, p.Comments) m := make(map[string]reflect.Value) pat := reflect.ValueOf(pattern) repl := reflect.ValueOf(replace) @@ -73,7 +74,9 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { } return val } - return apply(f, reflect.ValueOf(p)).Interface().(*ast.File) + r := apply(f, reflect.ValueOf(p)).Interface().(*ast.File) + r.Comments = cmap.Filter(r).Comments() // recreate comments list + return r } // setValue is a wrapper for x.SetValue(y); it protects diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go index 470c00625..e9a67a73a 100644 --- a/src/cmd/gofmt/simplify.go +++ b/src/cmd/gofmt/simplify.go @@ -10,7 +10,9 @@ import ( "reflect" ) -type simplifier struct{} +type simplifier struct { + hasDotImport bool // package file contains: import . "some/import/path" +} func (s *simplifier) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { @@ -34,7 +36,7 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { x = t.Value px = &t.Value } - simplify(x) + ast.Walk(s, x) // simplify x // if the element is a composite literal and its literal type // matches the outer literal's element type exactly, the inner // literal type may be omitted @@ -62,20 +64,54 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor { return nil } + case *ast.SliceExpr: + // a slice expression of the form: s[a:len(s)] + // can be simplified to: s[a:] + // if s is "simple enough" (for now we only accept identifiers) + if s.hasDotImport { + // if dot imports are present, we cannot be certain that an + // unresolved "len" identifier refers to the predefined len() + break + } + if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { + // the array/slice object is a single, resolved 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 { + // the len argument is the array/slice object + n.High = nil + } + } + } + } + // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] + // but we leave them as is since sometimes we want to be very explicit + // about the lower bound. + case *ast.RangeStmt: - // range of the form: for x, _ = range v {...} + // a range of the form: for x, _ = range v {...} // can be simplified to: for x = range v {...} - if n.Value != nil { - if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" { - n.Value = nil - } + if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name == "_" { + n.Value = nil } } return s } -func simplify(node ast.Node) { +func simplify(f *ast.File) { var s simplifier - ast.Walk(&s, node) + + // determine if f contains dot imports + for _, imp := range f.Imports { + if imp.Name != nil && imp.Name.Name == "." { + s.hasDotImport = true + break + } + } + + ast.Walk(&s, f) } diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden new file mode 100644 index 000000000..57679f770 --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.golden @@ -0,0 +1,12 @@ +/* + Source containing CR/LF line endings. + The gofmt'ed output must only have LF + line endings. +*/ +package main + +func main() { + // line comment + println("hello, world!") // another line comment + println() +} diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input new file mode 100644 index 000000000..61a1aa0b4 --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.input @@ -0,0 +1,12 @@ +/*
+ Source containing CR/LF line endings.
+ The gofmt'ed output must only have LF
+ line endings.
+*/
+package main
+
+func main() {
+ // line comment
+ println("hello, world!") // another line comment
+ println()
+}
diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden new file mode 100644 index 000000000..5a448a63d --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.golden @@ -0,0 +1,15 @@ +// 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. + +// Rewriting of expressions containing nodes with associated comments to +// expressions without those nodes must also eliminate the associated +// comments. + +package p + +func f(x int) int { + _ = 2 * x // this comment remains in the rewrite + _ = 2 * x + return 2 * x +} diff --git a/src/cmd/gofmt/testdata/rewrite5.input b/src/cmd/gofmt/testdata/rewrite5.input new file mode 100644 index 000000000..0d759e69b --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.input @@ -0,0 +1,15 @@ +// 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. + +// Rewriting of expressions containing nodes with associated comments to +// expressions without those nodes must also eliminate the associated +// comments. + +package p + +func f(x int) int { + _ = x + x // this comment remains in the rewrite + _ = x /* this comment must not be in the rewrite */ + x + return x /* this comment must not be in the rewrite */ + x +} diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden new file mode 100644 index 000000000..61e074f68 --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.golden @@ -0,0 +1,58 @@ +// Test cases for slice expression simplification. +package p + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices1.input b/src/cmd/gofmt/testdata/slices1.input new file mode 100644 index 000000000..4d2cbfff4 --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.input @@ -0,0 +1,58 @@ +// Test cases for slice expression simplification. +package p + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden new file mode 100644 index 000000000..433788e1e --- /dev/null +++ b/src/cmd/gofmt/testdata/slices2.golden @@ -0,0 +1,61 @@ +// Test cases for slice expression simplification. +// Because of a dot import, these slices must remain untouched. +package p + +import . "math" + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input new file mode 100644 index 000000000..433788e1e --- /dev/null +++ b/src/cmd/gofmt/testdata/slices2.input @@ -0,0 +1,61 @@ +// Test cases for slice expression simplification. +// Because of a dot import, these slices must remain untouched. +package p + +import . "math" + +var ( + a [10]byte + b [20]float32 + s []int + t struct { + s []byte + } + + _ = a[0:] + _ = a[1:10] + _ = a[2:len(a)] + _ = a[3:(len(a))] + _ = a[len(a) : len(a)-1] + _ = a[0:len(b)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + + _ = t.s[0:] + _ = t.s[1:10] + _ = t.s[2:len(t.s)] + _ = t.s[3:(len(t.s))] + _ = t.s[len(a) : len(t.s)-1] + _ = t.s[0:len(b)] + + _ = t.s[:] + _ = t.s[:10] + _ = t.s[:len(t.s)] + _ = t.s[:(len(t.s))] + _ = t.s[:len(t.s)-1] + _ = t.s[:len(b)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/typeswitch.golden b/src/cmd/gofmt/testdata/typeswitch.golden new file mode 100644 index 000000000..87e916181 --- /dev/null +++ b/src/cmd/gofmt/testdata/typeswitch.golden @@ -0,0 +1,60 @@ +/* + 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 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 + +func f() { + var x interface{} + switch x.(type) { // should remain the same + } + switch x.(type) { // should become: switch x.(type) { + } + + switch x.(type) { // should remain the same + case int: + } + switch x.(type) { // should become: switch x.(type) { + case int: + } + + switch x.(type) { // should remain the same + case []int: + } + + // Parenthesized (x.(type)) in type switches containing cases + // with unnamed (literal) types were never permitted by gofmt; + // thus there won't be any code in the wild using this style if + // the code was gofmt-ed. + /* + switch (x.(type)) { + case []int: + } + */ + + switch t := x.(type) { // should remain the same + default: + _ = t + } + + // Parenthesized (x.(type)) in type switches declaring a variable + // were never permitted by gofmt; thus there won't be any code in + // the wild using this style if the code was gofmt-ed. + /* + switch t := (x.(type)) { + default: + _ = t + } + */ +} diff --git a/src/cmd/gofmt/testdata/typeswitch.input b/src/cmd/gofmt/testdata/typeswitch.input new file mode 100644 index 000000000..f90f28949 --- /dev/null +++ b/src/cmd/gofmt/testdata/typeswitch.input @@ -0,0 +1,60 @@ +/* + 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 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 + +func f() { + var x interface{} + switch x.(type) { // should remain the same + } + switch (x.(type)) { // should become: switch x.(type) { + } + + switch x.(type) { // should remain the same + case int: + } + switch (x.(type)) { // should become: switch x.(type) { + case int: + } + + switch x.(type) { // should remain the same + case []int: + } + + // Parenthesized (x.(type)) in type switches containing cases + // with unnamed (literal) types were never permitted by gofmt; + // thus there won't be any code in the wild using this style if + // the code was gofmt-ed. + /* + switch (x.(type)) { + case []int: + } + */ + + switch t := x.(type) { // should remain the same + default: + _ = t + } + + // Parenthesized (x.(type)) in type switches declaring a variable + // were never permitted by gofmt; thus there won't be any code in + // the wild using this style if the code was gofmt-ed. + /* + switch t := (x.(type)) { + default: + _ = t + } + */ +} diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 786c10b64..6c6b1be43 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -34,6 +34,7 @@ #include "../ld/lib.h" #include "../ld/elf.h" #include "../ld/pe.h" +#include "../../pkg/runtime/mgc0.h" void dynreloc(void); static vlong addaddrplus4(Sym *s, Sym *t, int32 add); @@ -57,70 +58,73 @@ datcmp(Sym *s1, Sym *s2) } Sym* -datsort(Sym *l) +listsort(Sym *l, int (*cmp)(Sym*, Sym*), int off) { Sym *l1, *l2, *le; + #define NEXT(l) (*(Sym**)((char*)(l)+off)) - if(l == 0 || l->next == 0) + if(l == 0 || NEXT(l) == 0) return l; l1 = l; l2 = l; for(;;) { - l2 = l2->next; + l2 = NEXT(l2); if(l2 == 0) break; - l2 = l2->next; + l2 = NEXT(l2); if(l2 == 0) break; - l1 = l1->next; + l1 = NEXT(l1); } - l2 = l1->next; - l1->next = 0; - l1 = datsort(l); - l2 = datsort(l2); + l2 = NEXT(l1); + NEXT(l1) = 0; + l1 = listsort(l, cmp, off); + l2 = listsort(l2, cmp, off); /* set up lead element */ - if(datcmp(l1, l2) < 0) { + if(cmp(l1, l2) < 0) { l = l1; - l1 = l1->next; + l1 = NEXT(l1); } else { l = l2; - l2 = l2->next; + l2 = NEXT(l2); } le = l; for(;;) { if(l1 == 0) { while(l2) { - le->next = l2; + NEXT(le) = l2; le = l2; - l2 = l2->next; + l2 = NEXT(l2); } - le->next = 0; + NEXT(le) = 0; break; } if(l2 == 0) { while(l1) { - le->next = l1; + NEXT(le) = l1; le = l1; - l1 = l1->next; + l1 = NEXT(l1); } break; } - if(datcmp(l1, l2) < 0) { - le->next = l1; + if(cmp(l1, l2) < 0) { + NEXT(le) = l1; le = l1; - l1 = l1->next; + l1 = NEXT(l1); } else { - le->next = l2; + NEXT(le) = l2; le = l2; - l2 = l2->next; + l2 = NEXT(l2); } } - le->next = 0; + NEXT(le) = 0; return l; + + #undef NEXT } Reloc* @@ -145,21 +149,22 @@ void relocsym(Sym *s) { Reloc *r; + Sym *rs; Prog p; int32 i, off, siz, fl; vlong o; uchar *cast; - + cursym = s; memset(&p, 0, sizeof p); for(r=s->r; r<s->r+s->nr; r++) { off = r->off; siz = r->siz; - if(off < 0 || off+(siz&~Rbig) > s->np) { - diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz&~Rbig, 0, s->np); + if(off < 0 || off+siz > s->np) { + diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } - if(r->sym != S && (r->sym->type == 0 || r->sym->type == SXREF)) { + if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) { diag("%s: not defined", r->sym->name); continue; } @@ -175,18 +180,35 @@ relocsym(Sym *s) switch(r->type) { default: o = 0; - if(archreloc(r, s, &o) < 0) + if(isobj || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: o = symaddr(r->sym) + r->add; + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else { + // set up addend for eventual relocation via outer symbol + rs = r->sym; + while(rs->outer != nil) + rs = rs->outer; + o -= symaddr(rs); + } + } break; case D_PCREL: - // r->sym can be null when CALL $(constant) is transformed from absoulte PC to relative PC call. + // r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call. o = 0; if(r->sym) o += symaddr(r->sym); o += r->add - (s->value + r->off + r->siz); + if(isobj && r->sym->type != SCONST) { + if(thechar == '6') + o = 0; + else + o = r->add - r->siz; + } break; case D_SIZE: o = r->sym->size + r->add; @@ -197,20 +219,6 @@ relocsym(Sym *s) default: cursym = s; diag("bad reloc size %#ux for %s", siz, r->sym->name); - case 4 + Rbig: - fl = o; - s->p[off] = fl>>24; - s->p[off+1] = fl>>16; - s->p[off+2] = fl>>8; - s->p[off+3] = fl; - break; - case 4 + Rlittle: - fl = o; - s->p[off] = fl; - s->p[off+1] = fl>>8; - s->p[off+2] = fl>>16; - s->p[off+3] = fl>>24; - break; case 4: fl = o; cast = (uchar*)&fl; @@ -222,7 +230,7 @@ relocsym(Sym *s) for(i=0; i<8; i++) s->p[off+i] = cast[inuxi8[i]]; break; - } + } } } @@ -230,7 +238,7 @@ void reloc(void) { Sym *s; - + if(debug['v']) Bprint(&bso, "%5.2f reloc\n", cputime()); Bflush(&bso); @@ -245,10 +253,12 @@ void dynrelocsym(Sym *s) { Reloc *r; + Sym *rel; + Sym *got; if(HEADTYPE == Hwindows) { Sym *rel, *targ; - + rel = lookup(".rel", 0); if(s == rel) return; @@ -258,7 +268,7 @@ dynrelocsym(Sym *s) targ->plt = rel->size; r->sym = rel; r->add = targ->plt; - + // jmp *addr if(thechar == '8') { adduint8(rel, 0xff); @@ -281,16 +291,30 @@ dynrelocsym(Sym *s) return; } - for(r=s->r; r<s->r+s->nr; r++) - if(r->sym->type == SDYNIMPORT || r->type >= 256) + got = rel = nil; + if(flag_shared) { + rel = lookuprel(); + got = lookup(".got", 0); + } + s->rel_ro = 0; + for(r=s->r; r<s->r+s->nr; r++) { + if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) adddynrel(s, r); + if(flag_shared && r->sym != S && (r->sym->dynimpname == nil || r->sym->dynexport) && r->type == D_ADDR + && (s == got || s->type == SDATA || s->type == SGOSTRING || s->type == STYPE || s->type == SRODATA)) { + // Create address based RELATIVE relocation + adddynrela(rel, s, r); + if(s->type < SNOPTRDATA) + s->rel_ro = 1; + } + } } void dynreloc(void) { Sym *s; - + // -d supresses dynamic loader format, so we may as well not // compute these sections or mark their symbols as reachable. if(debug['d'] && HEADTYPE != Hwindows) @@ -363,12 +387,12 @@ savedata(Sym *s, Prog *p, char *pn) break; } break; - + case D_SCONST: for(i=0; i<siz; i++) s->p[off+i] = p->to.scon[i]; break; - + case D_CONST: if(p->to.sym) goto Addr; @@ -449,12 +473,12 @@ blk(Sym *start, int32 addr, int32 size) errorexit(); } } - + for(; addr < eaddr; addr++) cput(0); cflush(); } - + void codeblk(int32 addr, int32 size) { @@ -497,7 +521,7 @@ codeblk(int32 addr, int32 size) Bprint(&bso, "%.6llux\t%-20s | foreign text\n", (vlong)addr, sym->name); n = sym->size; q = sym->p; - + while(n >= 16) { Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); addr += 16; @@ -509,7 +533,7 @@ codeblk(int32 addr, int32 size) addr += n; continue; } - + Bprint(&bso, "%.6llux\t%-20s | %P\n", (vlong)sym->value, sym->name, p); for(p = p->link; p != P; p = p->link) { if(p->link != P) @@ -531,7 +555,7 @@ codeblk(int32 addr, int32 size) } Bflush(&bso); } - + void datblk(int32 addr, int32 size) { @@ -579,7 +603,7 @@ datblk(int32 addr, int32 size) void strnput(char *s, int n) { - for(; *s && n > 0; s++) { + for(; n > 0 && *s; s++) { cput(*s); n--; } @@ -594,18 +618,22 @@ addstrdata(char *name, char *value) { Sym *s, *sp; char *p; - + p = smprint("%s.str", name); sp = lookup(p, 0); free(p); addstring(sp, value); s = lookup(name, 0); + s->size = 0; s->dupok = 1; addaddr(s, sp); adduint32(s, strlen(value)); if(PtrSize == 8) adduint32(s, 0); // round struct to pointer width + + // in case reachability has already been computed + sp->reachable = s->reachable; } vlong @@ -628,41 +656,51 @@ addstring(Sym *s, char *str) } vlong -adduintxx(Sym *s, uint64 v, int wid) +setuintxx(Sym *s, vlong off, uint64 v, int wid) { - int32 i, r, fl; + int32 i, fl; vlong o; uchar *cast; if(s->type == 0) s->type = SDATA; s->reachable = 1; - r = s->size; - s->size += wid; - symgrow(s, s->size); - assert(r+wid <= s->size); + if(s->size < off+wid) { + s->size = off+wid; + symgrow(s, s->size); + } fl = v; cast = (uchar*)&fl; switch(wid) { case 1: - s->p[r] = cast[inuxi1[0]]; + s->p[off] = cast[inuxi1[0]]; break; case 2: for(i=0; i<2; i++) - s->p[r+i] = cast[inuxi2[i]]; + s->p[off+i] = cast[inuxi2[i]]; break; case 4: for(i=0; i<4; i++) - s->p[r+i] = cast[inuxi4[i]]; + s->p[off+i] = cast[inuxi4[i]]; break; case 8: o = v; cast = (uchar*)&o; for(i=0; i<8; i++) - s->p[r+i] = cast[inuxi8[i]]; + s->p[off+i] = cast[inuxi8[i]]; break; } - return r; + return off; +} + +vlong +adduintxx(Sym *s, uint64 v, int wid) +{ + int32 off; + + off = s->size; + setuintxx(s, off, v, wid); + return off; } vlong @@ -689,6 +727,30 @@ adduint64(Sym *s, uint64 v) return adduintxx(s, v, 8); } +void +setuint8(Sym *s, vlong r, uint8 v) +{ + setuintxx(s, r, v, 1); +} + +void +setuint16(Sym *s, vlong r, uint16 v) +{ + setuintxx(s, r, v, 2); +} + +void +setuint32(Sym *s, vlong r, uint32 v) +{ + setuintxx(s, r, v, 4); +} + +void +setuint64(Sym *s, vlong r, uint64 v) +{ + setuintxx(s, r, v, 8); +} + vlong addaddrplus(Sym *s, Sym *t, int32 add) { @@ -736,7 +798,7 @@ addpcrelplus(Sym *s, Sym *t, int32 add) { vlong i; Reloc *r; - + if(s->type == 0) s->type = SDATA; s->reachable = 1; @@ -759,6 +821,33 @@ addaddr(Sym *s, Sym *t) } vlong +setaddrplus(Sym *s, vlong off, Sym *t, int32 add) +{ + Reloc *r; + + if(s->type == 0) + s->type = SDATA; + s->reachable = 1; + if(off+PtrSize > s->size) { + s->size = off + PtrSize; + symgrow(s, s->size); + } + r = addrel(s); + r->sym = t; + r->off = off; + r->siz = PtrSize; + r->type = D_ADDR; + r->add = add; + return off; +} + +vlong +setaddr(Sym *s, vlong off, Sym *t) +{ + return setaddrplus(s, off, t, 0); +} + +vlong addsize(Sym *s, Sym *t) { vlong i; @@ -793,17 +882,91 @@ dosymtype(void) } } +static int32 +alignsymsize(int32 s) +{ + if(s >= 8) + s = rnd(s, 8); + else if(s >= PtrSize) + s = rnd(s, PtrSize); + else if(s > 2) + s = rnd(s, 4); + return s; +} + +static int32 +aligndatsize(int32 datsize, Sym *s) +{ + int32 t; + + if(s->align != 0) { + datsize = rnd(datsize, s->align); + } else { + t = alignsymsize(s->size); + if(t & 1) { + ; + } else if(t & 2) + datsize = rnd(datsize, 2); + else if(t & 4) + datsize = rnd(datsize, 4); + else + datsize = rnd(datsize, 8); + } + return datsize; +} + +static void +gcaddsym(Sym *gc, Sym *s, int32 off) +{ + int32 a; + Sym *gotype; + + if(s->size < PtrSize) + return; + if(strcmp(s->name, ".string") == 0) + return; + + gotype = s->gotype; + if(gotype != nil) { + //print("gcaddsym: %s %d %s\n", s->name, s->size, gotype->name); + adduintxx(gc, GC_CALL, PtrSize); + adduintxx(gc, off, PtrSize); + addpcrelplus(gc, decodetype_gc(gotype), 3*PtrSize+4); + if(PtrSize == 8) + adduintxx(gc, 0, 4); + } else { + //print("gcaddsym: %s %d <unknown type>\n", s->name, s->size); + for(a = -off&(PtrSize-1); a+PtrSize<=s->size; a+=PtrSize) { + adduintxx(gc, GC_APTR, PtrSize); + adduintxx(gc, off+a, PtrSize); + } + } +} + void dodata(void) { int32 t, datsize; - Section *sect, *noptr; + Section *sect; Sym *s, *last, **l; + Sym *gcdata1, *gcbss1; if(debug['v']) Bprint(&bso, "%5.2f dodata\n", cputime()); Bflush(&bso); + // define garbage collection symbols + gcdata1 = lookup("gcdata1", 0); + gcdata1->type = SGCDATA; + gcdata1->reachable = 1; + gcbss1 = lookup("gcbss1", 0); + gcbss1->type = SGCBSS; + gcbss1->reachable = 1; + + // size of .data and .bss section. the zero value is later replaced by the actual size of the section. + adduintxx(gcdata1, 0, PtrSize); + adduintxx(gcbss1, 0, PtrSize); + last = nil; datap = nil; @@ -834,7 +997,7 @@ dodata(void) * symbol, which is itself data. */ dynreloc(); - + /* some symbols may no longer belong in datap (Mach-O) */ for(l=&datap; (s=*l) != nil; ) { if(s->type <= STEXT || SXREF <= s->type) @@ -844,19 +1007,150 @@ dodata(void) } *l = nil; - datap = datsort(datap); + if(flag_shared) { + for(s=datap; s != nil; s = s->next) { + if(s->rel_ro) + s->type = SDATARELRO; + } + } + datap = listsort(datap, datcmp, offsetof(Sym, next)); /* - * allocate data sections. list is sorted by type, + * allocate sections. list is sorted by type, * so we can just walk it for each piece we want to emit. + * segdata is processed before segtext, because we need + * to see all symbols in the .data and .bss sections in order + * to generate garbage collection information. */ + /* begin segdata */ + + /* skip symbols belonging to segtext */ + s = datap; + for(; s != nil && s->type < SELFSECT; s = s->next) + ; + + /* writable ELF sections */ + datsize = 0; + for(; s != nil && s->type < SNOPTRDATA; s = s->next) { + sect = addsection(&segdata, s->name, 06); + if(s->align != 0) + datsize = rnd(datsize, s->align); + sect->vaddr = datsize; + s->sect = sect; + s->type = SDATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + sect->len = datsize - sect->vaddr; + } + + /* pointer-free data */ + sect = addsection(&segdata, ".noptrdata", 06); + sect->vaddr = datsize; + lookup("noptrdata", 0)->sect = sect; + lookup("enoptrdata", 0)->sect = sect; + for(; s != nil && s->type < SDATARELRO; s = s->next) { + s->sect = sect; + s->type = SDATA; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* dynamic relocated rodata */ + if(flag_shared) { + sect = addsection(&segdata, ".data.rel.ro", 06); + sect->vaddr = datsize; + lookup("datarelro", 0)->sect = sect; + lookup("edatarelro", 0)->sect = sect; + for(; s != nil && s->type == SDATARELRO; s = s->next) { + if(s->align != 0) + datsize = rnd(datsize, s->align); + s->sect = sect; + s->type = SDATA; + s->value = datsize; + datsize += rnd(s->size, PtrSize); + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + } + + /* data */ + sect = addsection(&segdata, ".data", 06); + sect->vaddr = datsize; + lookup("data", 0)->sect = sect; + lookup("edata", 0)->sect = sect; + for(; s != nil && s->type < SBSS; s = s->next) { + if(s->type == SDATARELRO) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + s->sect = sect; + s->type = SDATA; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + gcaddsym(gcdata1, s, datsize - sect->vaddr); // gc + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + adduintxx(gcdata1, GC_END, PtrSize); + setuintxx(gcdata1, 0, sect->len, PtrSize); + + /* bss */ + sect = addsection(&segdata, ".bss", 06); + sect->vaddr = datsize; + lookup("bss", 0)->sect = sect; + lookup("ebss", 0)->sect = sect; + for(; s != nil && s->type < SNOPTRBSS; s = s->next) { + s->sect = sect; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + gcaddsym(gcbss1, s, datsize - sect->vaddr); // gc + datsize += t; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + adduintxx(gcbss1, GC_END, PtrSize); + setuintxx(gcbss1, 0, sect->len, PtrSize); + + /* pointer-free bss */ + sect = addsection(&segdata, ".noptrbss", 06); + sect->vaddr = datsize; + lookup("noptrbss", 0)->sect = sect; + lookup("enoptrbss", 0)->sect = sect; + for(; s != nil; s = s->next) { + if(s->type > SNOPTRBSS) { + cursym = s; + diag("unexpected symbol type %d", s->type); + } + s->sect = sect; + t = alignsymsize(s->size); + datsize = aligndatsize(datsize, s); + s->value = datsize; + datsize += t; + } + sect->len = datsize - sect->vaddr; + lookup("end", 0)->sect = sect; + + /* we finished segdata, begin segtext */ + /* read-only data */ sect = addsection(&segtext, ".rodata", 04); sect->vaddr = 0; + lookup("rodata", 0)->sect = sect; + lookup("erodata", 0)->sect = sect; datsize = 0; s = datap; - for(; s != nil && s->type < SSYMTAB; s = s->next) { + for(; s != nil && s->type < STYPELINK; s = s->next) { + s->sect = sect; if(s->align != 0) datsize = rnd(datsize, s->align); s->type = SRODATA; @@ -864,11 +1158,57 @@ dodata(void) datsize += rnd(s->size, PtrSize); } sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* type */ + sect = addsection(&segtext, ".typelink", 04); + sect->vaddr = datsize; + lookup("typelink", 0)->sect = sect; + lookup("etypelink", 0)->sect = sect; + for(; s != nil && s->type == STYPELINK; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* gcdata */ + sect = addsection(&segtext, ".gcdata", 04); + sect->vaddr = datsize; + lookup("gcdata", 0)->sect = sect; + lookup("egcdata", 0)->sect = sect; + for(; s != nil && s->type == SGCDATA; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); + + /* gcbss */ + sect = addsection(&segtext, ".gcbss", 04); + sect->vaddr = datsize; + lookup("gcbss", 0)->sect = sect; + lookup("egcbss", 0)->sect = sect; + for(; s != nil && s->type == SGCBSS; s = s->next) { + s->sect = sect; + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + datsize = rnd(datsize, PtrSize); /* gosymtab */ sect = addsection(&segtext, ".gosymtab", 04); sect->vaddr = datsize; + lookup("symtab", 0)->sect = sect; + lookup("esymtab", 0)->sect = sect; for(; s != nil && s->type < SPCLNTAB; s = s->next) { + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += s->size; @@ -879,7 +1219,10 @@ dodata(void) /* gopclntab */ sect = addsection(&segtext, ".gopclntab", 04); sect->vaddr = datsize; + lookup("pclntab", 0)->sect = sect; + lookup("epclntab", 0)->sect = sect; for(; s != nil && s->type < SELFROSECT; s = s->next) { + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += s->size; @@ -893,101 +1236,12 @@ dodata(void) if(s->align != 0) datsize = rnd(datsize, s->align); sect->vaddr = datsize; + s->sect = sect; s->type = SRODATA; s->value = datsize; datsize += rnd(s->size, PtrSize); sect->len = datsize - sect->vaddr; } - - /* writable ELF sections */ - datsize = 0; - for(; s != nil && s->type < SNOPTRDATA; s = s->next) { - sect = addsection(&segdata, s->name, 06); - if(s->align != 0) - datsize = rnd(datsize, s->align); - sect->vaddr = datsize; - s->type = SDATA; - s->value = datsize; - datsize += rnd(s->size, PtrSize); - sect->len = datsize - sect->vaddr; - } - - /* pointer-free data, then data */ - sect = addsection(&segdata, ".noptrdata", 06); - sect->vaddr = datsize; - noptr = sect; - for(; ; s = s->next) { - if((s == nil || s->type >= SDATA) && sect == noptr) { - // finish noptrdata, start data - datsize = rnd(datsize, 8); - sect->len = datsize - sect->vaddr; - sect = addsection(&segdata, ".data", 06); - sect->vaddr = datsize; - } - if(s == nil || s->type >= SBSS) { - // finish data - sect->len = datsize - sect->vaddr; - break; - } - s->type = SDATA; - t = s->size; - if(t >= PtrSize) - t = rnd(t, PtrSize); - else if(t > 2) - t = rnd(t, 4); - if(s->align != 0) - datsize = rnd(datsize, s->align); - else if(t & 1) { - ; - } else if(t & 2) - datsize = rnd(datsize, 2); - else if(t & 4) - datsize = rnd(datsize, 4); - else - datsize = rnd(datsize, 8); - s->value = datsize; - datsize += t; - } - - /* bss, then pointer-free bss */ - noptr = nil; - sect = addsection(&segdata, ".bss", 06); - sect->vaddr = datsize; - for(; ; s = s->next) { - if((s == nil || s->type >= SNOPTRBSS) && noptr == nil) { - // finish bss, start noptrbss - datsize = rnd(datsize, 8); - sect->len = datsize - sect->vaddr; - sect = addsection(&segdata, ".noptrbss", 06); - sect->vaddr = datsize; - noptr = sect; - } - if(s == nil) { - sect->len = datsize - sect->vaddr; - break; - } - if(s->type > SNOPTRBSS) { - cursym = s; - diag("unexpected symbol type %d", s->type); - } - t = s->size; - if(t >= PtrSize) - t = rnd(t, PtrSize); - else if(t > 2) - t = rnd(t, 4); - if(s->align != 0) - datsize = rnd(datsize, s->align); - else if(t & 1) { - ; - } else if(t & 2) - datsize = rnd(datsize, 2); - else if(t & 4) - datsize = rnd(datsize, 4); - else - datsize = rnd(datsize, 8); - s->value = datsize; - datsize += t; - } } // assign addresses to text @@ -1002,16 +1256,21 @@ textaddress(void) addsection(&segtext, ".text", 05); // Assign PCs in text segment. - // Could parallelize, by assigning to text + // Could parallelize, by assigning to text // and then letting threads copy down, but probably not worth it. sect = segtext.sect; + lookup("text", 0)->sect = sect; + lookup("etext", 0)->sect = sect; va = INITTEXT; sect->vaddr = va; for(sym = textp; sym != nil; sym = sym->next) { + sym->sect = sect; if(sym->type & SSUB) continue; if(sym->align != 0) va = rnd(va, sym->align); + else if(sym->text != P) + va = rnd(va, FuncAlign); sym->value = 0; for(sub = sym; sub != S; sub = sub->sub) { sub->value += va; @@ -1023,7 +1282,7 @@ textaddress(void) } va += sym->size; } - + // Align end of code so that rodata starts aligned. // 128 bytes is likely overkill but definitely cheap. va = rnd(va, 128); @@ -1035,7 +1294,8 @@ textaddress(void) void address(void) { - Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss; + Section *s, *text, *data, *rodata, *symtab, *pclntab, *noptr, *bss, *noptrbss, *datarelro; + Section *gcdata, *gcbss, *typelink; Sym *sym, *sub; uvlong va; @@ -1058,12 +1318,13 @@ address(void) segdata.filelen = 0; if(HEADTYPE == Hwindows) segdata.fileoff = segtext.fileoff + rnd(segtext.len, PEFILEALIGN); - if(HEADTYPE == Hplan9x32) + if(HEADTYPE == Hplan9x64 || HEADTYPE == Hplan9x32) segdata.fileoff = segtext.fileoff + segtext.filelen; data = nil; noptr = nil; bss = nil; noptrbss = nil; + datarelro = nil; for(s=segdata.sect; s != nil; s=s->next) { s->vaddr = va; va += s->len; @@ -1077,12 +1338,17 @@ address(void) bss = s; if(strcmp(s->name, ".noptrbss") == 0) noptrbss = s; + if(strcmp(s->name, ".data.rel.ro") == 0) + datarelro = s; } segdata.filelen -= bss->len + noptrbss->len; // deduct .bss text = segtext.sect; rodata = text->next; - symtab = rodata->next; + typelink = rodata->next; + gcdata = typelink->next; + gcbss = gcdata->next; + symtab = gcbss->next; pclntab = symtab->next; for(sym = datap; sym != nil; sym = sym->next) { @@ -1094,11 +1360,21 @@ address(void) for(sub = sym->sub; sub != nil; sub = sub->sub) sub->value += sym->value; } - + xdefine("text", STEXT, text->vaddr); xdefine("etext", STEXT, text->vaddr + text->len); xdefine("rodata", SRODATA, rodata->vaddr); xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("typelink", SRODATA, typelink->vaddr); + xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len); + if(datarelro != nil) { + xdefine("datarelro", SRODATA, datarelro->vaddr); + xdefine("edatarelro", SRODATA, datarelro->vaddr + datarelro->len); + } + xdefine("gcdata", SGCDATA, gcdata->vaddr); + xdefine("egcdata", SGCDATA, gcdata->vaddr + gcdata->len); + xdefine("gcbss", SGCBSS, gcbss->vaddr); + xdefine("egcbss", SGCBSS, gcbss->vaddr + gcbss->len); xdefine("symtab", SRODATA, symtab->vaddr); xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); xdefine("pclntab", SRODATA, pclntab->vaddr); diff --git a/src/cmd/ld/decodesym.c b/src/cmd/ld/decodesym.c new file mode 100644 index 000000000..ab3f4fbd5 --- /dev/null +++ b/src/cmd/ld/decodesym.c @@ -0,0 +1,215 @@ +// 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. + +#include "l.h" +#include "lib.h" +#include "../../pkg/runtime/typekind.h" + +// Decoding the type.* symbols. This has to be in sync with +// ../../pkg/runtime/type.go, or more specificaly, with what +// ../gc/reflect.c stuffs in these. + +static Reloc* +decode_reloc(Sym *s, int32 off) +{ + int i; + + for (i = 0; i < s->nr; i++) + if (s->r[i].off == off) + return s->r + i; + return nil; +} + +static Sym* +decode_reloc_sym(Sym *s, int32 off) +{ + Reloc *r; + + r = decode_reloc(s,off); + if (r == nil) + return nil; + return r->sym; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uint64 v; + uint32 l; + uchar *cast, *inuxi; + int i; + + v = l = 0; + cast = nil; + inuxi = nil; + switch (sz) { + case 2: + cast = (uchar*)&l; + inuxi = inuxi2; + break; + case 4: + cast = (uchar*)&l; + inuxi = inuxi4; + break; + case 8: + cast = (uchar*)&v; + inuxi = inuxi8; + break; + default: + diag("dwarf: decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + cast[inuxi[i]] = p[i]; + if (sz == 8) + return v; + return l; +} + +// Type.commonType.kind +uint8 +decodetype_kind(Sym *s) +{ + return s->p[1*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f +} + +// Type.commonType.size +vlong +decodetype_size(Sym *s) +{ + return decode_inuxi(s->p, PtrSize); // 0x8 / 0x10 +} + +// Type.commonType.gc +Sym* +decodetype_gc(Sym *s) +{ + return decode_reloc_sym(s, 1*PtrSize + 8 + 1*PtrSize); +} + +// Type.ArrayType.elem and Type.SliceType.Elem +Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +vlong +decodetype_arraylen(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); +} + +// Type.PtrType.elem +Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.MapType.key, elem +Sym* +decodetype_mapkey(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} +Sym* +decodetype_mapvalue(Sym *s) +{ + return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 +} + +// Type.ChanType.elem +Sym* +decodetype_chanelem(Sym *s) +{ + return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 +} + +// Type.FuncType.dotdotdot +int +decodetype_funcdotdotdot(Sym *s) +{ + return s->p[CommonSize]; +} + +// Type.FuncType.in.len +int +decodetype_funcincount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+2*PtrSize, IntSize); +} + +int +decodetype_funcoutcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*IntSize, IntSize); +} + +Sym* +decodetype_funcintype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + PtrSize); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +Sym* +decodetype_funcouttype(Sym *s, int i) +{ + Reloc *r; + + r = decode_reloc(s, CommonSize + 2*PtrSize + 2*IntSize); + if (r == nil) + return nil; + return decode_reloc_sym(r->sym, r->add + i * PtrSize); +} + +// Type.StructType.fields.Slice::len +int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, IntSize); +} + +enum { + StructFieldSize = 5*PtrSize +}; +// Type.StructType.fields[]-> name, typ and offset. +char* +decodetype_structfieldname(Sym *s, int i) +{ + Reloc *r; + + // go.string."foo" 0x28 / 0x40 + s = decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + i*StructFieldSize); + if (s == nil) // embedded structs have a nil name. + return nil; + r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 + if (r == nil) // shouldn't happen. + return nil; + return (char*) r->sym->p + r->add; // the c-string +} + +Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc_sym(s, CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 2*PtrSize); +} + +vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + CommonSize + PtrSize + 2*IntSize + i*StructFieldSize + 4*PtrSize, IntSize); +} + +// InterfaceTYpe.methods.len +vlong +decodetype_ifacemethodcount(Sym *s) +{ + return decode_inuxi(s->p + CommonSize + PtrSize, IntSize); +} diff --git a/src/cmd/ld/doc.go b/src/cmd/ld/doc.go index e99e50466..bad4e540f 100644 --- a/src/cmd/ld/doc.go +++ b/src/cmd/ld/doc.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. +// +build ignore + /* Ld is the portable code for a modified version of the Plan 9 linker. The original is documented at - http://plan9.bell-labs.com/magic/man2html/1/2l + http://plan9.bell-labs.com/magic/man2html/1/8l It reads object files (.5, .6, or .8 files) and writes a binary named for the architecture (5.out, 6.out, 8.out) by default (if $GOOS is windows, a .exe suffix @@ -33,7 +35,7 @@ Options new in this version: Write Apple Mach-O binaries (default when $GOOS is darwin) -Hlinux Write Linux ELF binaries (default when $GOOS is linux) - -Hfreebsd (only in 6l/8l) + -Hfreebsd Write FreeBSD ELF binaries (default when $GOOS is freebsd) -Hnetbsd (only in 6l/8l) Write NetBSD ELF binaries (default when $GOOS is netbsd) @@ -56,5 +58,18 @@ Options new in this version: Set the value of an otherwise uninitialized string variable. The symbol name should be of the form importpath.name, as displayed in the symbol table printed by "go tool nm". + -race + Link with race detection libraries. + -B value + Add a NT_GNU_BUILD_ID note when using ELF. The value + should start with 0x and be an even number of hex digits. + -Z + Zero stack on function entry. This is expensive but it might + be useful in cases where you are suffering from false positives + during garbage collection and are willing to trade the CPU time + for getting rid of the false positives. + NOTE: it only eliminates false positives caused by other function + calls, not false positives caused by dead temporaries stored in + the current function call. */ -package documentation +package main diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 57e5a4283..d6a357e49 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -19,6 +19,7 @@ #include "../ld/elf.h" #include "../ld/macho.h" #include "../ld/pe.h" +#include "../../pkg/runtime/typekind.h" /* * Offsets and sizes of the debug_* sections in the cout file. @@ -740,241 +741,6 @@ newabslocexprattr(DWDie *die, vlong addr) memmove(die->attr->data, block, i); } -// Decoding the type.* symbols. This has to be in sync with -// ../../pkg/runtime/type.go, or more specificaly, with what -// ../gc/reflect.c stuffs in these. - -enum { - KindBool = 1, - KindInt, - KindInt8, - KindInt16, - KindInt32, - KindInt64, - KindUint, - KindUint8, - KindUint16, - KindUint32, - KindUint64, - KindUintptr, - KindFloat32, - KindFloat64, - KindComplex64, - KindComplex128, - KindArray, - KindChan, - KindFunc, - KindInterface, - KindMap, - KindPtr, - KindSlice, - KindString, - KindStruct, - KindUnsafePointer, - - KindNoPointers = 1<<7, - - // size of Type interface header + CommonType structure. - CommonSize = 2*PtrSize+ 5*PtrSize + 8, -}; - -static Reloc* -decode_reloc(Sym *s, int32 off) -{ - int i; - - for (i = 0; i < s->nr; i++) - if (s->r[i].off == off) - return s->r + i; - return nil; -} - -static Sym* -decode_reloc_sym(Sym *s, int32 off) -{ - Reloc *r; - - r = decode_reloc(s,off); - if (r == nil) - return nil; - return r->sym; -} - -static uvlong -decode_inuxi(uchar* p, int sz) -{ - uint64 v; - uint32 l; - uchar *cast, *inuxi; - int i; - - v = l = 0; - cast = nil; - inuxi = nil; - switch (sz) { - case 2: - cast = (uchar*)&l; - inuxi = inuxi2; - break; - case 4: - cast = (uchar*)&l; - inuxi = inuxi4; - break; - case 8: - cast = (uchar*)&v; - inuxi = inuxi8; - break; - default: - diag("dwarf: decode inuxi %d", sz); - errorexit(); - } - for (i = 0; i < sz; i++) - cast[inuxi[i]] = p[i]; - if (sz == 8) - return v; - return l; -} - -// Type.commonType.kind -static uint8 -decodetype_kind(Sym *s) -{ - return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f -} - -// Type.commonType.size -static vlong -decodetype_size(Sym *s) -{ - return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 -} - -// Type.ArrayType.elem and Type.SliceType.Elem -static Sym* -decodetype_arrayelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -static vlong -decodetype_arraylen(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); -} - -// Type.PtrType.elem -static Sym* -decodetype_ptrelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.MapType.key, elem -static Sym* -decodetype_mapkey(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} -static Sym* -decodetype_mapvalue(Sym *s) -{ - return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 -} - -// Type.ChanType.elem -static Sym* -decodetype_chanelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.FuncType.dotdotdot -static int -decodetype_funcdotdotdot(Sym *s) -{ - return s->p[CommonSize]; -} - -// Type.FuncType.in.len -static int -decodetype_funcincount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+2*PtrSize, 4); -} - -static int -decodetype_funcoutcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*4, 4); -} - -static Sym* -decodetype_funcintype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + PtrSize); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -static Sym* -decodetype_funcouttype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + 2*PtrSize + 2*4); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -// Type.StructType.fields.Slice::len -static int -decodetype_structfieldcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - -enum { - StructFieldSize = 5*PtrSize -}; -// Type.StructType.fields[]-> name, typ and offset. -static char* -decodetype_structfieldname(Sym *s, int i) -{ - Reloc *r; - - // go.string."foo" 0x28 / 0x40 - s = decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize); - if (s == nil) // embedded structs have a nil name. - return nil; - r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 - if (r == nil) // shouldn't happen. - return nil; - return (char*) r->sym->p + r->add; // the c-string -} - -static Sym* -decodetype_structfieldtype(Sym *s, int i) -{ - return decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize + 2*PtrSize); -} - -static vlong -decodetype_structfieldoffs(Sym *s, int i) -{ - return decode_inuxi(s->p + CommonSize + PtrSize + 2*4 + i*StructFieldSize + 4*PtrSize, 4); -} - -// InterfaceTYpe.methods.len -static vlong -decodetype_ifacemethodcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - // Fake attributes for slices, maps and channel enum { @@ -1531,6 +1297,10 @@ decodez(char *s) return 0; r = malloc(len + 1); + if(r == nil) { + diag("out of memory"); + errorexit(); + } rb = r; re = rb + len + 1; @@ -1709,6 +1479,10 @@ inithist(Auto *a) continue; if (linehist == 0 || linehist->absline != absline) { Linehist* lh = malloc(sizeof *lh); + if(lh == nil) { + diag("out of memory"); + errorexit(); + } lh->link = linehist; lh->absline = absline; linehist = lh; @@ -2162,7 +1936,8 @@ writeinfo(void) * because we need die->offs and infoo/infosize; */ static int -ispubname(DWDie *die) { +ispubname(DWDie *die) +{ DWAttr *a; switch(die->abbrev) { @@ -2175,7 +1950,8 @@ ispubname(DWDie *die) { } static int -ispubtype(DWDie *die) { +ispubtype(DWDie *die) +{ return die->abbrev >= DW_ABRV_NULLTYPE; } @@ -2316,7 +2092,7 @@ dwarfemitdebugsections(void) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); // Needed by the prettyprinter code for interface inspection. - defgotype(lookup_or_diag("type.runtime.commonType")); + defgotype(lookup_or_diag("type.runtime.rtype")); defgotype(lookup_or_diag("type.runtime.interfaceType")); defgotype(lookup_or_diag("type.runtime.itab")); diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index de9e6b854..630906653 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -11,7 +11,7 @@ * in order to write the code just once. The 64-bit data structure is * written in the 32-bit format on the 32-bit machines. */ -#define NSECT 32 +#define NSECT 48 int iself; @@ -31,6 +31,8 @@ struct Elfstring static Elfstring elfstr[100]; static int nelfstr; +static char buildinfo[32]; + /* Initialize the global variable that describes the ELF header. It will be updated as we write section and prog headers. @@ -52,6 +54,11 @@ elfinit(void) break; // 32-bit architectures + case '5': + // we only use EABI on linux/arm + if(HEADTYPE == Hlinux) + hdr.flags = 0x5000002; // has entry point, Version5 EABI + // fallthrough default: hdr.phoff = ELF32HDRSIZE; /* Must be be ELF32HDRSIZE: first PHdr must follow ELF header */ hdr.shoff = ELF32HDRSIZE; /* Will move as we add PHeaders */ @@ -190,19 +197,13 @@ newElfPhdr(void) } ElfShdr* -newElfShstrtab(vlong name) -{ - hdr.shstrndx = hdr.shnum; - return newElfShdr(name); -} - -ElfShdr* newElfShdr(vlong name) { ElfShdr *e; e = mal(sizeof *e); e->name = name; + e->shnum = hdr.shnum; if (hdr.shnum >= NSECT) { diag("too many shdrs"); } else { @@ -332,26 +333,53 @@ elfinterp(ElfShdr *sh, uint64 startva, uint64 resoff, char *p) } int -elfwriteinterp(vlong stridx) +elfwriteinterp(void) { - ElfShdr *sh = nil; - int i; - - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; - if(sh == nil || interp == nil) - return 0; - + ElfShdr *sh; + + sh = elfshname(".interp"); cseek(sh->off); cwrite(interp, sh->size); return sh->size; } -// Defined in NetBSD's sys/exec_elf.h -#define ELF_NOTE_TYPE_NETBSD_TAG 1 +int +elfnote(ElfShdr *sh, uint64 startva, uint64 resoff, int sz) +{ + uint64 n; + + n = sizeof(Elf_Note) + sz + resoff % 4; + + sh->type = SHT_NOTE; + sh->flags = SHF_ALLOC; + sh->addralign = 4; + sh->addr = startva + resoff - n; + sh->off = resoff - n; + sh->size = n; + + return n; +} + +ElfShdr * +elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag) +{ + ElfShdr *sh; + + sh = elfshname(str); + + // Write Elf_Note header. + cseek(sh->off); + LPUT(namesz); + LPUT(descsz); + LPUT(tag); + + return sh; +} + +// NetBSD Signature (as per sys/exec_elf.h) #define ELF_NOTE_NETBSD_NAMESZ 7 #define ELF_NOTE_NETBSD_DESCSZ 4 +#define ELF_NOTE_NETBSD_TAG 1 #define ELF_NOTE_NETBSD_NAME "NetBSD\0\0" #define ELF_NOTE_NETBSD_VERSION 599000000 /* NetBSD 5.99 */ @@ -360,37 +388,131 @@ elfnetbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) { int n; - n = sizeof(Elf_Note) + ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; - n += resoff % 4; - sh->addr = startva + resoff - n; - sh->off = resoff - n; - sh->size = n; - - return n; + n = ELF_NOTE_NETBSD_NAMESZ + ELF_NOTE_NETBSD_DESCSZ + 1; + return elfnote(sh, startva, resoff, n); } int -elfwritenetbsdsig(vlong stridx) { - ElfShdr *sh = nil; - int i; +elfwritenetbsdsig(void) +{ + ElfShdr *sh; - for(i = 0; i < hdr.shnum; i++) - if(shdr[i]->name == stridx) - sh = shdr[i]; + // Write Elf_Note header. + sh = elfwritenotehdr(".note.netbsd.ident", ELF_NOTE_NETBSD_NAMESZ, ELF_NOTE_NETBSD_DESCSZ, ELF_NOTE_NETBSD_TAG); if(sh == nil) return 0; - // Write Elf_Note header followed by NetBSD string. - cseek(sh->off); - LPUT(ELF_NOTE_NETBSD_NAMESZ); - LPUT(ELF_NOTE_NETBSD_DESCSZ); - LPUT(ELF_NOTE_TYPE_NETBSD_TAG); - cwrite(ELF_NOTE_NETBSD_NAME, 8); + // Followed by NetBSD string and version. + cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1); LPUT(ELF_NOTE_NETBSD_VERSION); return sh->size; } +// OpenBSD Signature +#define ELF_NOTE_OPENBSD_NAMESZ 8 +#define ELF_NOTE_OPENBSD_DESCSZ 4 +#define ELF_NOTE_OPENBSD_TAG 1 +#define ELF_NOTE_OPENBSD_NAME "OpenBSD\0" +#define ELF_NOTE_OPENBSD_VERSION 0 + +int +elfopenbsdsig(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_OPENBSD_NAMESZ + ELF_NOTE_OPENBSD_DESCSZ; + return elfnote(sh, startva, resoff, n); +} + +int +elfwriteopenbsdsig(void) +{ + ElfShdr *sh; + + // Write Elf_Note header. + sh = elfwritenotehdr(".note.openbsd.ident", ELF_NOTE_OPENBSD_NAMESZ, ELF_NOTE_OPENBSD_DESCSZ, ELF_NOTE_OPENBSD_TAG); + if(sh == nil) + return 0; + + // Followed by OpenBSD string and version. + cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); + LPUT(ELF_NOTE_OPENBSD_VERSION); + + return sh->size; +} + +void +addbuildinfo(char *val) +{ + char *ov; + int i, b, j; + + if(val[0] != '0' || val[1] != 'x') { + fprint(2, "%s: -B argument must start with 0x: %s\n", argv0, val); + exits("usage"); + } + ov = val; + val += 2; + i = 0; + while(*val != '\0') { + if(val[1] == '\0') { + fprint(2, "%s: -B argument must have even number of digits: %s\n", argv0, ov); + exits("usage"); + } + b = 0; + for(j = 0; j < 2; j++, val++) { + b *= 16; + if(*val >= '0' && *val <= '9') + b += *val - '0'; + else if(*val >= 'a' && *val <= 'f') + b += *val - 'a' + 10; + else if(*val >= 'A' && *val <= 'F') + b += *val - 'A' + 10; + else { + fprint(2, "%s: -B argument contains invalid hex digit %c: %s\n", argv0, *val, ov); + exits("usage"); + } + } + if(i >= nelem(buildinfo)) { + fprint(2, "%s: -B option too long (max %d digits): %s\n", argv0, (int)nelem(buildinfo), ov); + exits("usage"); + } + buildinfo[i++] = b; + } + buildinfolen = i; +} + +// Build info note +#define ELF_NOTE_BUILDINFO_NAMESZ 4 +#define ELF_NOTE_BUILDINFO_TAG 3 +#define ELF_NOTE_BUILDINFO_NAME "GNU\0" + +int +elfbuildinfo(ElfShdr *sh, uint64 startva, uint64 resoff) +{ + int n; + + n = ELF_NOTE_BUILDINFO_NAMESZ + rnd(buildinfolen, 4); + return elfnote(sh, startva, resoff, n); +} + +int +elfwritebuildinfo(void) +{ + ElfShdr *sh; + + sh = elfwritenotehdr(".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, buildinfolen, ELF_NOTE_BUILDINFO_TAG); + if(sh == nil) + return 0; + + cwrite(ELF_NOTE_BUILDINFO_NAME, ELF_NOTE_BUILDINFO_NAMESZ); + cwrite(buildinfo, buildinfolen); + cwrite("\0\0\0", rnd(buildinfolen, 4) - buildinfolen); + + return sh->size; +} + extern int nelfsym; int elfverneed; @@ -584,18 +706,18 @@ elfphload(Segment *seg) } ElfShdr* -elfshbits(Section *sect) +elfshname(char *name) { int i, off; ElfShdr *sh; for(i=0; i<nelfstr; i++) { - if(strcmp(sect->name, elfstr[i].s) == 0) { + if(strcmp(name, elfstr[i].s) == 0) { off = elfstr[i].off; goto found; } } - diag("cannot find elf name %s", sect->name); + diag("cannot find elf name %s", name); errorexit(); return nil; @@ -605,8 +727,30 @@ found: if(sh->name == off) return sh; } - + sh = newElfShdr(off); + return sh; +} + +ElfShdr* +elfshalloc(Section *sect) +{ + ElfShdr *sh; + + sh = elfshname(sect->name); + sect->elfsect = sh; + return sh; +} + +ElfShdr* +elfshbits(Section *sect) +{ + ElfShdr *sh; + + sh = elfshalloc(sect); + if(sh->type > 0) + return sh; + if(sect->vaddr < sect->seg->vaddr + sect->seg->filelen) sh->type = SHT_PROGBITS; else @@ -616,10 +760,732 @@ found: sh->flags |= SHF_EXECINSTR; if(sect->rwx & 2) sh->flags |= SHF_WRITE; - sh->addr = sect->vaddr; + if(!isobj) + sh->addr = sect->vaddr; sh->addralign = PtrSize; sh->size = sect->len; sh->off = sect->seg->fileoff + sect->vaddr - sect->seg->vaddr; + + return sh; +} + +ElfShdr* +elfshreloc(Section *sect) +{ + int typ; + ElfShdr *sh; + char *prefix; + char buf[100]; + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return nil; + if(strcmp(sect->name, ".shstrtab") == 0) + return nil; + + if(thechar == '6') { + prefix = ".rela"; + typ = SHT_RELA; + } else { + prefix = ".rel"; + typ = SHT_REL; + } + + snprint(buf, sizeof buf, "%s%s", prefix, sect->name); + sh = elfshname(buf); + sh->type = typ; + sh->entsize = PtrSize*(2+(typ==SHT_RELA)); + sh->link = elfshname(".symtab")->shnum; + sh->info = sect->elfsect->shnum; + sh->off = sect->reloff; + sh->size = sect->rellen; + sh->addralign = PtrSize; return sh; } + +void +elfrelocsect(Section *sect, Sym *first) +{ + Sym *sym, *rs; + int32 eaddr; + Reloc *r; + int64 add; + + // If main section is SHT_NOBITS, nothing to relocate. + // Also nothing to relocate in .shstrtab. + if(sect->vaddr >= sect->seg->vaddr + sect->seg->filelen) + return; + if(strcmp(sect->name, ".shstrtab") == 0) + return; + + sect->reloff = cpos(); + for(sym = first; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= sect->vaddr) + break; + } + + eaddr = sect->vaddr + sect->len; + for(; sym != nil; sym = sym->next) { + if(!sym->reachable) + continue; + if(sym->value >= eaddr) + break; + cursym = sym; + + for(r = sym->r; r < sym->r+sym->nr; r++) { + // Ignore relocations handled by reloc already. + switch(r->type) { + case D_SIZE: + continue; + case D_ADDR: + case D_PCREL: + if(r->sym->type == SCONST) + continue; + break; + } + + add = r->add; + rs = r->sym; + while(rs->outer != nil) { + add += rs->value - rs->outer->value; + rs = rs->outer; + } + + if(rs->elfsym == 0) + diag("reloc %d to non-elf symbol %s (rs=%s) %d", r->type, r->sym->name, rs->name, rs->type); + + if(elfreloc1(r, sym->value - sect->vaddr + r->off, rs->elfsym, add) < 0) + diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); + } + } + + sect->rellen = cpos() - sect->reloff; +} + +void +elfemitreloc(void) +{ + Section *sect; + + while(cpos()&7) + cput(0); + + elfrelocsect(segtext.sect, textp); + for(sect=segtext.sect->next; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfrelocsect(sect, datap); +} + +void +doelf(void) +{ + Sym *s, *shstrtab, *dynstr; + + if(!iself) + return; + + /* predefine strings we need for section headers */ + shstrtab = lookup(".shstrtab", 0); + shstrtab->type = SELFROSECT; + shstrtab->reachable = 1; + + addstring(shstrtab, ""); + addstring(shstrtab, ".text"); + addstring(shstrtab, ".noptrdata"); + addstring(shstrtab, ".data"); + addstring(shstrtab, ".bss"); + addstring(shstrtab, ".noptrbss"); + if(HEADTYPE == Hnetbsd) + addstring(shstrtab, ".note.netbsd.ident"); + if(HEADTYPE == Hopenbsd) + addstring(shstrtab, ".note.openbsd.ident"); + if(buildinfolen > 0) + addstring(shstrtab, ".note.gnu.build-id"); + addstring(shstrtab, ".elfdata"); + addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".typelink"); + if(flag_shared) + addstring(shstrtab, ".data.rel.ro"); + addstring(shstrtab, ".gcdata"); + addstring(shstrtab, ".gcbss"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); + + if(isobj) { + debug['s'] = 0; + debug['d'] = 1; + + if(thechar == '6') { + addstring(shstrtab, ".rela.text"); + addstring(shstrtab, ".rela.rodata"); + addstring(shstrtab, ".rela.typelink"); + addstring(shstrtab, ".rela.gcdata"); + addstring(shstrtab, ".rela.gcbss"); + addstring(shstrtab, ".rela.gosymtab"); + addstring(shstrtab, ".rela.gopclntab"); + addstring(shstrtab, ".rela.noptrdata"); + addstring(shstrtab, ".rela.data"); + } else { + addstring(shstrtab, ".rel.text"); + addstring(shstrtab, ".rel.rodata"); + addstring(shstrtab, ".rel.typelink"); + addstring(shstrtab, ".rel.gcdata"); + addstring(shstrtab, ".rel.gcbss"); + addstring(shstrtab, ".rel.gosymtab"); + addstring(shstrtab, ".rel.gopclntab"); + addstring(shstrtab, ".rel.noptrdata"); + addstring(shstrtab, ".rel.data"); + } + } + + if(!debug['s']) { + addstring(shstrtab, ".symtab"); + addstring(shstrtab, ".strtab"); + dwarfaddshstrings(shstrtab); + } + addstring(shstrtab, ".shstrtab"); + + if(!debug['d']) { /* -d suppresses dynamic loader format */ + addstring(shstrtab, ".interp"); + addstring(shstrtab, ".hash"); + addstring(shstrtab, ".got"); + addstring(shstrtab, ".got.plt"); + addstring(shstrtab, ".dynamic"); + addstring(shstrtab, ".dynsym"); + addstring(shstrtab, ".dynstr"); + if(thechar == '6') { + addstring(shstrtab, ".rela"); + addstring(shstrtab, ".rela.plt"); + } else { + addstring(shstrtab, ".rel"); + addstring(shstrtab, ".rel.plt"); + } + addstring(shstrtab, ".plt"); + addstring(shstrtab, ".gnu.version"); + addstring(shstrtab, ".gnu.version_r"); + + /* dynamic symbol table - first entry all zeros */ + s = lookup(".dynsym", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(thechar == '6') + s->size += ELF64SYMSIZE; + else + s->size += ELF32SYMSIZE; + + /* dynamic string table */ + s = lookup(".dynstr", 0); + s->type = SELFROSECT; + s->reachable = 1; + if(s->size == 0) + addstring(s, ""); + dynstr = s; + + /* relocation table */ + if(thechar == '6') + s = lookup(".rela", 0); + else + s = lookup(".rel", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* global offset table */ + s = lookup(".got", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + /* hash */ + s = lookup(".hash", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".got.plt", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + s = lookup(".plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + elfsetupplt(); + + if(thechar == '6') + s = lookup(".rela.plt", 0); + else + s = lookup(".rel.plt", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version", 0); + s->reachable = 1; + s->type = SELFROSECT; + + s = lookup(".gnu.version_r", 0); + s->reachable = 1; + s->type = SELFROSECT; + + /* define dynamic elf table */ + s = lookup(".dynamic", 0); + s->reachable = 1; + s->type = SELFSECT; // writable + + /* + * .dynamic table + */ + elfwritedynentsym(s, DT_HASH, lookup(".hash", 0)); + elfwritedynentsym(s, DT_SYMTAB, lookup(".dynsym", 0)); + if(thechar == '6') + elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); + else + elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); + elfwritedynentsym(s, DT_STRTAB, lookup(".dynstr", 0)); + elfwritedynentsymsize(s, DT_STRSZ, lookup(".dynstr", 0)); + if(thechar == '6') { + elfwritedynentsym(s, DT_RELA, lookup(".rela", 0)); + elfwritedynentsymsize(s, DT_RELASZ, lookup(".rela", 0)); + elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); + } else { + elfwritedynentsym(s, DT_REL, lookup(".rel", 0)); + elfwritedynentsymsize(s, DT_RELSZ, lookup(".rel", 0)); + elfwritedynent(s, DT_RELENT, ELF32RELSIZE); + } + if(rpath) + elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); + + elfwritedynentsym(s, DT_PLTGOT, lookup(".got.plt", 0)); + + if(thechar == '6') { + elfwritedynent(s, DT_PLTREL, DT_RELA); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0)); + } else { + elfwritedynent(s, DT_PLTREL, DT_REL); + elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0)); + elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0)); + } + + elfwritedynent(s, DT_DEBUG, 0); + + if(flag_shared) { + Sym *init_sym = lookup(LIBINITENTRY, 0); + if(init_sym->type != STEXT) + diag("entry not text: %s", init_sym->name); + elfwritedynentsym(s, DT_INIT, init_sym); + } + + // Do not write DT_NULL. elfdynhash will finish it. + } +} + +void +shsym(ElfShdr *sh, Sym *s) +{ + vlong addr; + addr = symaddr(s); + if(sh->flags&SHF_ALLOC) + sh->addr = addr; + sh->off = datoff(addr); + sh->size = s->size; +} + +void +phsh(ElfPhdr *ph, ElfShdr *sh) +{ + ph->vaddr = sh->addr; + ph->paddr = ph->vaddr; + ph->off = sh->off; + ph->filesz = sh->size; + ph->memsz = sh->size; + ph->align = sh->addralign; +} + +void +asmbelfsetup(void) +{ + Section *sect; + + /* This null SHdr must appear before all others */ + elfshname(""); + + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshalloc(sect); +} + +void +asmbelf(vlong symo) +{ + int a, o; + vlong startva, resoff; + ElfEhdr *eh; + ElfPhdr *ph, *pph, *pnote; + ElfShdr *sh; + Section *sect; + + eh = getElfEhdr(); + switch(thechar) { + default: + diag("unknown architecture in asmbelf"); + errorexit(); + case '5': + eh->machine = EM_ARM; + break; + case '6': + eh->machine = EM_X86_64; + break; + case '8': + eh->machine = EM_386; + break; + } + + startva = INITTEXT - HEADR; + resoff = ELFRESERVE; + + pph = nil; + if(isobj) { + /* skip program headers */ + eh->phoff = 0; + eh->phentsize = 0; + goto elfobj; + } + + /* program header info */ + pph = newElfPhdr(); + pph->type = PT_PHDR; + pph->flags = PF_R + PF_X; + pph->off = eh->ehsize; + pph->vaddr = INITTEXT - HEADR + pph->off; + pph->paddr = INITTEXT - HEADR + pph->off; + pph->align = INITRND; + + /* + * PHDR must be in a loaded segment. Adjust the text + * segment boundaries downwards to include it. + */ + o = segtext.vaddr - pph->vaddr; + segtext.vaddr -= o; + segtext.len += o; + o = segtext.fileoff - pph->off; + segtext.fileoff -= o; + segtext.filelen += o; + + if(!debug['d']) { + /* interpreter */ + sh = elfshname(".interp"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + if(interpreter == nil) { + switch(HEADTYPE) { + case Hlinux: + interpreter = linuxdynld; + break; + case Hfreebsd: + interpreter = freebsddynld; + break; + case Hnetbsd: + interpreter = netbsddynld; + break; + case Hopenbsd: + interpreter = openbsddynld; + break; + } + } + resoff -= elfinterp(sh, startva, resoff, interpreter); + + ph = newElfPhdr(); + ph->type = PT_INTERP; + ph->flags = PF_R; + phsh(ph, sh); + } + + pnote = nil; + if(HEADTYPE == Hnetbsd || HEADTYPE == Hopenbsd) { + sh = nil; + switch(HEADTYPE) { + case Hnetbsd: + sh = elfshname(".note.netbsd.ident"); + resoff -= elfnetbsdsig(sh, startva, resoff); + break; + case Hopenbsd: + sh = elfshname(".note.openbsd.ident"); + resoff -= elfopenbsdsig(sh, startva, resoff); + break; + } + + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + phsh(pnote, sh); + } + + if(buildinfolen > 0) { + sh = elfshname(".note.gnu.build-id"); + resoff -= elfbuildinfo(sh, startva, resoff); + + if(pnote == nil) { + pnote = newElfPhdr(); + pnote->type = PT_NOTE; + pnote->flags = PF_R; + } + phsh(pnote, sh); + } + + // Additions to the reserved area must be above this line. + USED(resoff); + + elfphload(&segtext); + elfphload(&segdata); + + /* Dynamic linking sections */ + if(!debug['d']) { /* -d suppresses dynamic loader format */ + sh = elfshname(".dynsym"); + sh->type = SHT_DYNSYM; + sh->flags = SHF_ALLOC; + if(PtrSize == 8) + sh->entsize = ELF64SYMSIZE; + else + sh->entsize = ELF32SYMSIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + // sh->info = index of first non-local symbol (number of local symbols) + shsym(sh, lookup(".dynsym", 0)); + + sh = elfshname(".dynstr"); + sh->type = SHT_STRTAB; + sh->flags = SHF_ALLOC; + sh->addralign = 1; + shsym(sh, lookup(".dynstr", 0)); + + if(elfverneed) { + sh = elfshname(".gnu.version"); + sh->type = SHT_GNU_VERSYM; + sh->flags = SHF_ALLOC; + sh->addralign = 2; + sh->link = elfshname(".dynsym")->shnum; + sh->entsize = 2; + shsym(sh, lookup(".gnu.version", 0)); + + sh = elfshname(".gnu.version_r"); + sh->type = SHT_GNU_VERNEED; + sh->flags = SHF_ALLOC; + sh->addralign = PtrSize; + sh->info = elfverneed; + sh->link = elfshname(".dynstr")->shnum; + shsym(sh, lookup(".gnu.version_r", 0)); + } + + switch(eh->machine) { + case EM_X86_64: + sh = elfshname(".rela.plt"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + sh->info = elfshname(".plt")->shnum; + shsym(sh, lookup(".rela.plt", 0)); + + sh = elfshname(".rela"); + sh->type = SHT_RELA; + sh->flags = SHF_ALLOC; + sh->entsize = ELF64RELASIZE; + sh->addralign = 8; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rela", 0)); + break; + + default: + sh = elfshname(".rel.plt"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel.plt", 0)); + + sh = elfshname(".rel"); + sh->type = SHT_REL; + sh->flags = SHF_ALLOC; + sh->entsize = ELF32RELSIZE; + sh->addralign = 4; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".rel", 0)); + break; + } + + sh = elfshname(".plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_EXECINSTR; + if(eh->machine == EM_X86_64) + sh->entsize = 16; + else + sh->entsize = 4; + sh->addralign = 4; + shsym(sh, lookup(".plt", 0)); + + sh = elfshname(".got"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got", 0)); + + sh = elfshname(".got.plt"); + sh->type = SHT_PROGBITS; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = PtrSize; + sh->addralign = PtrSize; + shsym(sh, lookup(".got.plt", 0)); + + sh = elfshname(".hash"); + sh->type = SHT_HASH; + sh->flags = SHF_ALLOC; + sh->entsize = 4; + sh->addralign = PtrSize; + sh->link = elfshname(".dynsym")->shnum; + shsym(sh, lookup(".hash", 0)); + + /* sh and PT_DYNAMIC for .dynamic section */ + sh = elfshname(".dynamic"); + sh->type = SHT_DYNAMIC; + sh->flags = SHF_ALLOC+SHF_WRITE; + sh->entsize = 2*PtrSize; + sh->addralign = PtrSize; + sh->link = elfshname(".dynstr")->shnum; + shsym(sh, lookup(".dynamic", 0)); + ph = newElfPhdr(); + ph->type = PT_DYNAMIC; + ph->flags = PF_R + PF_W; + phsh(ph, sh); + + /* + * Thread-local storage segment (really just size). + */ + // Do not emit PT_TLS for OpenBSD since ld.so(1) does + // not currently support it. This is handled + // appropriately in runtime/cgo. + if(tlsoffset != 0 && HEADTYPE != Hopenbsd) { + ph = newElfPhdr(); + ph->type = PT_TLS; + ph->flags = PF_R; + ph->memsz = -tlsoffset; + ph->align = PtrSize; + } + } + + if(HEADTYPE == Hlinux) { + ph = newElfPhdr(); + ph->type = PT_GNU_STACK; + ph->flags = PF_W+PF_R; + ph->align = PtrSize; + + ph = newElfPhdr(); + ph->type = PT_PAX_FLAGS; + ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled + ph->align = PtrSize; + } + +elfobj: + sh = elfshname(".shstrtab"); + sh->type = SHT_STRTAB; + sh->addralign = 1; + shsym(sh, lookup(".shstrtab", 0)); + eh->shstrndx = sh->shnum; + + // put these sections early in the list + if(!debug['s']) { + elfshname(".symtab"); + elfshname(".strtab"); + } + + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshbits(sect); + + if(isobj) { + for(sect=segtext.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + for(sect=segdata.sect; sect!=nil; sect=sect->next) + elfshreloc(sect); + } + + if(!debug['s']) { + sh = elfshname(".symtab"); + sh->type = SHT_SYMTAB; + sh->off = symo; + sh->size = symsize; + sh->addralign = PtrSize; + sh->entsize = 8+2*PtrSize; + sh->link = elfshname(".strtab")->shnum; + sh->info = elfglobalsymndx; + + sh = elfshname(".strtab"); + sh->type = SHT_STRTAB; + sh->off = symo+symsize; + sh->size = elfstrsize; + sh->addralign = 1; + + // TODO(rsc): Enable for isobj too, once we know it works. + if(!isobj) + dwarfaddelfheaders(); + } + + /* Main header */ + eh->ident[EI_MAG0] = '\177'; + eh->ident[EI_MAG1] = 'E'; + eh->ident[EI_MAG2] = 'L'; + eh->ident[EI_MAG3] = 'F'; + if(HEADTYPE == Hfreebsd) + eh->ident[EI_OSABI] = ELFOSABI_FREEBSD; + else if(HEADTYPE == Hnetbsd) + eh->ident[EI_OSABI] = ELFOSABI_NETBSD; + else if(HEADTYPE == Hopenbsd) + eh->ident[EI_OSABI] = ELFOSABI_OPENBSD; + if(PtrSize == 8) + eh->ident[EI_CLASS] = ELFCLASS64; + else + eh->ident[EI_CLASS] = ELFCLASS32; + eh->ident[EI_DATA] = ELFDATA2LSB; + eh->ident[EI_VERSION] = EV_CURRENT; + + if(flag_shared) + eh->type = ET_DYN; + else if(isobj) + eh->type = ET_REL; + else + eh->type = ET_EXEC; + + if(!isobj) + eh->entry = entryvalue(); + + eh->version = EV_CURRENT; + + if(pph != nil) { + pph->filesz = eh->phnum * eh->phentsize; + pph->memsz = pph->filesz; + } + + cseek(0); + a = 0; + a += elfwritehdr(); + a += elfwritephdrs(); + a += elfwriteshdrs(); + if(!debug['d']) + a += elfwriteinterp(); + if(!isobj) { + if(HEADTYPE == Hnetbsd) + a += elfwritenetbsdsig(); + if(HEADTYPE == Hopenbsd) + a += elfwriteopenbsdsig(); + if(buildinfolen > 0) + a += elfwritebuildinfo(); + } + if(a > ELFRESERVE) + diag("ELFRESERVE too small: %d > %d", a, ELFRESERVE); +} diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 690ade975..3e22125b2 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -251,6 +251,7 @@ typedef struct { #define PT_LOPROC 0x70000000 /* First processor-specific type. */ #define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ #define PT_GNU_STACK 0x6474e551 +#define PT_PAX_FLAGS 0x65041580 /* Values for p_flags. */ #define PF_X 0x1 /* Executable. */ @@ -562,6 +563,10 @@ typedef struct { #define R_ARM_GOTPC 25 /* Add PC-relative GOT table address. */ #define R_ARM_GOT32 26 /* Add PC-relative GOT offset. */ #define R_ARM_PLT32 27 /* Add PC-relative PLT offset. */ +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_V4BX 40 +#define R_ARM_GOT_PREL 96 #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_RSBREL32 250 @@ -571,7 +576,7 @@ typedef struct { #define R_ARM_RPC24 254 #define R_ARM_RBASE 255 -#define R_ARM_COUNT 33 /* Count of defined relocation types. */ +#define R_ARM_COUNT 37 /* Count of defined relocation types. */ #define R_386_NONE 0 /* No relocation. */ @@ -836,7 +841,8 @@ typedef struct { * Section header. */ -typedef struct { +typedef struct Elf64_Shdr Elf64_Shdr; +struct Elf64_Shdr { Elf64_Word name; /* Section name (index into the section header string table). */ Elf64_Word type; /* Section type. */ @@ -848,7 +854,9 @@ typedef struct { Elf64_Word info; /* Depends on section type. */ Elf64_Xword addralign; /* Alignment in bytes. */ Elf64_Xword entsize; /* Size of each entry in section. */ -} Elf64_Shdr; + + int shnum; /* section number, not stored on disk */ +}; /* * Program header. @@ -952,7 +960,6 @@ typedef Elf64_Phdr ElfPhdr; void elfinit(void); ElfEhdr *getElfEhdr(void); -ElfShdr *newElfShstrtab(vlong); ElfShdr *newElfShdr(vlong); ElfPhdr *newElfPhdr(void); uint32 elfwritehdr(void); @@ -969,18 +976,40 @@ extern int numelfshdr; extern int iself; extern int elfverneed; int elfinterp(ElfShdr*, uint64, uint64, char*); -int elfwriteinterp(vlong); +int elfwriteinterp(void); int elfnetbsdsig(ElfShdr*, uint64, uint64); -int elfwritenetbsdsig(vlong); +int elfwritenetbsdsig(void); +int elfopenbsdsig(ElfShdr*, uint64, uint64); +int elfwriteopenbsdsig(void); +void addbuildinfo(char*); +int elfbuildinfo(ElfShdr*, uint64, uint64); +int elfwritebuildinfo(void); void elfdynhash(void); ElfPhdr* elfphload(Segment*); ElfShdr* elfshbits(Section*); +ElfShdr* elfshalloc(Section*); +ElfShdr* elfshname(char*); +ElfShdr* elfshreloc(Section*); void elfsetstring(char*, int); void elfaddverneed(Sym*); +void elfemitreloc(void); +void shsym(ElfShdr*, Sym*); +void phsh(ElfPhdr*, ElfShdr*); +void doelf(void); +void elfsetupplt(void); +void dwarfaddshstrings(Sym*); +void dwarfaddelfheaders(void); +void asmbelf(vlong symo); +void asmbelfsetup(void); +extern char linuxdynld[]; +extern char freebsddynld[]; +extern char netbsddynld[]; +extern char openbsddynld[]; +int elfreloc1(Reloc*, vlong off, int32 elfsym, vlong add); EXTERN int elfstrsize; EXTERN char* elfstrdat; -EXTERN int elftextsh; +EXTERN int buildinfolen; /* * Total amount of space to reserve at the start of the file diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 3271be1f5..b2527b13e 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -67,8 +67,7 @@ ilookup(char *name) } static void loadpkgdata(char*, char*, char*, int); -static void loaddynimport(char*, char*, char*, int); -static void loaddynexport(char*, char*, char*, int); +static void loadcgo(char*, char*, char*, int); static int parsemethod(char**, char*, char**); static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**); @@ -177,12 +176,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) loadpkgdata(filename, pkg, p0, p1 - p0); - // look for dynimport section - p0 = strstr(p1, "\n$$ // dynimport"); + // look for cgo section + p0 = strstr(p1, "\n$$ // cgo"); if(p0 != nil) { p0 = strchr(p0+1, '\n'); if(p0 == nil) { - fprint(2, "%s: found $$ // dynimport but no newline in %s\n", argv0, filename); + fprint(2, "%s: found $$ // cgo but no newline in %s\n", argv0, filename); if(debug['u']) errorexit(); return; @@ -191,34 +190,12 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename, int whence) if(p1 == nil) p1 = strstr(p0, "\n!\n"); if(p1 == nil) { - fprint(2, "%s: cannot find end of // dynimport section in %s\n", argv0, filename); + fprint(2, "%s: cannot find end of // cgo section in %s\n", argv0, filename); if(debug['u']) errorexit(); return; } - loaddynimport(filename, pkg, p0 + 1, p1 - (p0+1)); - } - - // look for dynexp section - p0 = strstr(p1, "\n$$ // dynexport"); - if(p0 != nil) { - p0 = strchr(p0+1, '\n'); - if(p0 == nil) { - fprint(2, "%s: found $$ // dynexporg but no newline in %s\n", argv0, filename); - if(debug['u']) - errorexit(); - return; - } - p1 = strstr(p0, "\n$$"); - if(p1 == nil) - p1 = strstr(p0, "\n!\n"); - if(p1 == nil) { - fprint(2, "%s: cannot find end of // dynexporg section in %s\n", argv0, filename); - if(debug['u']) - errorexit(); - return; - } - loaddynexport(filename, pkg, p0 + 1, p1 - (p0+1)); + loadcgo(filename, pkg, p0 + 1, p1 - (p0+1)); } } @@ -409,10 +386,16 @@ parsemethod(char **pp, char *ep, char **methp) if(p == ep) return 0; + // might be a comment about the method + if(p + 2 < ep && strncmp(p, "//", 2) == 0) + goto useline; + // if it says "func (", it's a method - if(p + 6 >= ep || strncmp(p, "func (", 6) != 0) - return 0; + if(p + 6 < ep && strncmp(p, "func (", 6) == 0) + goto useline; + return 0; +useline: // definition to end of line *methp = p; while(p < ep && *p != '\n') @@ -428,173 +411,182 @@ parsemethod(char **pp, char *ep, char **methp) } static void -loaddynimport(char *file, char *pkg, char *p, int n) +loadcgo(char *file, char *pkg, char *p, int n) { - char *pend, *next, *name, *def, *p0, *lib, *q; + char *pend, *next, *p0, *q; + char *f[10], *local, *remote, *lib; + int nf; Sym *s; USED(file); pend = p + n; + p0 = nil; for(; p<pend; p=next) { next = strchr(p, '\n'); if(next == nil) next = ""; else *next++ = '\0'; - p0 = p; - if(strncmp(p, "dynimport ", 10) != 0) - goto err; - p += 10; - name = p; - p = strchr(name, ' '); - if(p == nil) - goto err; - while(*p == ' ') - p++; - def = p; - p = strchr(def, ' '); - if(p == nil) - goto err; - while(*p == ' ') - p++; - lib = p; - // successful parse: now can edit the line - *strchr(name, ' ') = 0; - *strchr(def, ' ') = 0; + free(p0); + p0 = strdup(p); // save for error message + nf = tokenize(p, f, nelem(f)); - if(debug['d']) { - fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); - nerrors++; - return; - } + if(strcmp(f[0], "cgo_import_dynamic") == 0) { + if(nf < 2 || nf > 4) + goto err; + + local = f[1]; + remote = local; + if(nf > 2) + remote = f[2]; + lib = ""; + if(nf > 3) + lib = f[3]; + + if(debug['d']) { + fprint(2, "%s: %s: cannot use dynamic imports with -d flag\n", argv0, file); + nerrors++; + return; + } - if(strcmp(name, "_") == 0 && strcmp(def, "_") == 0) { - // allow #pragma dynimport _ _ "foo.so" - // to force a link of foo.so. - havedynamic = 1; - adddynlib(lib); + if(strcmp(local, "_") == 0 && strcmp(remote, "_") == 0) { + // allow #pragma dynimport _ _ "foo.so" + // to force a link of foo.so. + havedynamic = 1; + adddynlib(lib); + continue; + } + + local = expandpkg(local, pkg); + q = strchr(remote, '#'); + if(q) + *q++ = '\0'; + s = lookup(local, 0); + if(local != f[1]) + free(local); + if(s->type == 0 || s->type == SXREF) { + s->dynimplib = lib; + s->dynimpname = remote; + s->dynimpvers = q; + s->type = SDYNIMPORT; + havedynamic = 1; + } continue; } - - name = expandpkg(name, pkg); - q = strchr(def, '@'); - if(q) - *q++ = '\0'; - s = lookup(name, 0); - free(name); - if(s->type == 0 || s->type == SXREF) { - s->dynimplib = lib; - s->dynimpname = def; - s->dynimpvers = q; - s->type = SDYNIMPORT; - havedynamic = 1; + + if(strcmp(f[0], "cgo_import_static") == 0) { + if(nf != 2) + goto err; + if(isobj) { + local = f[1]; + s = lookup(local, 0); + s->type = SHOSTOBJ; + s->size = 0; + } + continue; } - } - return; -err: - fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); - nerrors++; -} - -static void -loaddynexport(char *file, char *pkg, char *p, int n) -{ - char *pend, *next, *local, *elocal, *remote, *p0; - Sym *s; - - USED(file); - pend = p + n; - for(; p<pend; p=next) { - next = strchr(p, '\n'); - if(next == nil) - next = ""; - else - *next++ = '\0'; - p0 = p; - if(strncmp(p, "dynexport ", 10) != 0) - goto err; - p += 10; - local = p; - p = strchr(local, ' '); - if(p == nil) - goto err; - while(*p == ' ') - p++; - remote = p; - - // successful parse: now can edit the line - *strchr(local, ' ') = 0; + if(strcmp(f[0], "cgo_export") == 0) { + if(nf < 2 || nf > 3) + goto err; + local = f[1]; + if(nf > 2) + remote = f[2]; + else + remote = local; + local = expandpkg(local, pkg); + s = lookup(local, 0); + if(s->dynimplib != nil) { + fprint(2, "%s: symbol is both imported and exported: %s\n", argv0, local); + nerrors++; + } + s->dynimpname = remote; + s->dynexport = 1; - elocal = expandpkg(local, pkg); + if(ndynexp%32 == 0) + dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); + dynexp[ndynexp++] = s; - s = lookup(elocal, 0); - if(s->dynimplib != nil) { - fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local); - nerrors++; + if(local != f[1]) + free(local); + continue; + } + + if(strcmp(f[0], "cgo_dynamic_linker") == 0) { + if(nf != 2) + goto err; + + if(!debug['I']) { // not overridden by command line + if(interpreter != nil && strcmp(interpreter, f[1]) != 0) { + fprint(2, "%s: conflict dynlinker: %s and %s\n", argv0, interpreter, f[1]); + nerrors++; + return; + } + free(interpreter); + interpreter = strdup(f[1]); + } + continue; } - s->dynimpname = remote; - s->dynexport = 1; - - if(ndynexp%32 == 0) - dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]); - dynexp[ndynexp++] = s; - - if (elocal != local) - free(elocal); } + free(p0); return; err: - fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0); + fprint(2, "%s: %s: invalid dynimport line: %s\n", argv0, file, p0); nerrors++; } -static int markdepth; +static Sym *markq; +static Sym *emarkq; static void -marktext(Sym *s) +mark1(Sym *s, Sym *parent) { - Auto *a; - Prog *p; - - if(s == S) + if(s == S || s->reachable) return; - markdepth++; - if(debug['v'] > 1) - Bprint(&bso, "%d marktext %s\n", markdepth, s->name); - for(a=s->autom; a; a=a->link) - mark(a->gotype); - for(p=s->text; p != P; p=p->link) { - if(p->from.sym) - mark(p->from.sym); - if(p->to.sym) - mark(p->to.sym); - } - markdepth--; + if(strncmp(s->name, "go.weak.", 8) == 0) + return; + s->reachable = 1; + s->reachparent = parent; + if(markq == nil) + markq = s; + else + emarkq->queue = s; + emarkq = s; } void mark(Sym *s) { - int i; + mark1(s, nil); +} - if(s == S || s->reachable) - return; - if(strncmp(s->name, "weak.", 5) == 0) - return; - s->reachable = 1; - if(s->text) - marktext(s); - for(i=0; i<s->nr; i++) - mark(s->r[i].sym); - if(s->gotype) - mark(s->gotype); - if(s->sub) - mark(s->sub); - if(s->outer) - mark(s->outer); +static void +markflood(void) +{ + Auto *a; + Prog *p; + Sym *s; + int i; + + for(s=markq; s!=S; s=s->queue) { + if(s->text) { + if(debug['v'] > 1) + Bprint(&bso, "marktext %s\n", s->name); + for(a=s->autom; a; a=a->link) + mark1(a->gotype, s); + for(p=s->text; p != P; p=p->link) { + mark1(p->from.sym, s); + mark1(p->to.sym, s); + } + } + for(i=0; i<s->nr; i++) + mark1(s->r[i].sym, s); + mark1(s->gotype, s); + mark1(s->sub, s); + mark1(s->outer, s); + } } static char* @@ -651,19 +643,30 @@ void deadcode(void) { int i; - Sym *s, *last; + Sym *s, *last, *p; Auto *z; + Fmt fmt; if(debug['v']) Bprint(&bso, "%5.2f deadcode\n", cputime()); mark(lookup(INITENTRY, 0)); + if(flag_shared) + mark(lookup(LIBINITENTRY, 0)); for(i=0; i<nelem(morename); i++) mark(lookup(morename[i], 0)); for(i=0; i<ndynexp; i++) mark(dynexp[i]); + + markflood(); + // keep each beginning with 'typelink.' if the symbol it points at is being kept. + for(s = allsym; s != S; s = s->allsym) { + if(strncmp(s->name, "go.typelink.", 12) == 0) + s->reachable = s->nr==1 && s->r[0].sym->reachable; + } + // remove dead text but keep file information (z symbols). last = nil; z = nil; @@ -690,11 +693,34 @@ deadcode(void) last->next = nil; for(s = allsym; s != S; s = s->allsym) - if(strncmp(s->name, "weak.", 5) == 0) { + if(strncmp(s->name, "go.weak.", 8) == 0) { s->special = 1; // do not lay out in data segment s->reachable = 1; s->hide = 1; } + + // record field tracking references + fmtstrinit(&fmt); + for(s = allsym; s != S; s = s->allsym) { + if(strncmp(s->name, "go.track.", 9) == 0) { + s->special = 1; // do not lay out in data segment + s->hide = 1; + if(s->reachable) { + fmtprint(&fmt, "%s", s->name+9); + for(p=s->reachparent; p; p=p->reachparent) + fmtprint(&fmt, "\t%s", p->name); + fmtprint(&fmt, "\n"); + } + s->type = SCONST; + s->value = 0; + } + } + if(tracksym == nil) + return; + s = lookup(tracksym, 0); + if(!s->reachable) + return; + addstrdata(tracksym, fmtstrflush(&fmt)); } void @@ -705,11 +731,12 @@ doweak(void) // resolve weak references only if // target symbol will be in binary anyway. for(s = allsym; s != S; s = s->allsym) { - if(strncmp(s->name, "weak.", 5) == 0) { - t = rlookup(s->name+5, s->version); + if(strncmp(s->name, "go.weak.", 8) == 0) { + t = rlookup(s->name+8, s->version); if(t && t->type != 0 && t->reachable) { s->value = t->value; s->type = t->type; + s->outer = t; } else { s->type = SCONST; s->value = 0; @@ -871,3 +898,28 @@ importcycles(void) for(p=pkgall; p; p=p->all) cycle(p); } + +static int +scmp(const void *p1, const void *p2) +{ + Sym *s1, *s2; + + s1 = *(Sym**)p1; + s2 = *(Sym**)p2; + return strcmp(s1->dynimpname, s2->dynimpname); +} +void +sortdynexp(void) +{ + int i; + + // On Mac OS X Mountain Lion, we must sort exported symbols + // So we sort them here and pre-allocate dynid for them + // See http://golang.org/issue/4029 + if(HEADTYPE != Hdarwin) + return; + qsort(dynexp, ndynexp, sizeof dynexp[0], scmp); + for(i=0; i<ndynexp; i++) { + dynexp[i]->dynid = -i-100; // also known to [68]l/asm.c:^adddynsym + } +} diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index bd4f3e7d8..2bbf4f83e 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -308,9 +308,19 @@ uchar ElfMagic[4] = { 0x7F, 'E', 'L', 'F' }; static ElfSect* section(ElfObj*, char*); static int map(ElfObj*, ElfSect*); -static int readsym(ElfObj*, int i, ElfSym*); +static int readsym(ElfObj*, int i, ElfSym*, int); static int reltype(char*, int, uchar*); +int +valuecmp(Sym *a, Sym *b) +{ + if(a->value < b->value) + return -1; + if(a->value > b->value) + return +1; + return 0; +} + void ldelf(Biobuf *f, char *pkg, int64 len, char *pn) { @@ -327,8 +337,10 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) Endian *e; Reloc *r, *rp; Sym *s; + Sym **symbols; + + symbols = nil; - USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); @@ -516,7 +528,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) if(sect->type != ElfSectNobits && map(obj, sect) < 0) goto bad; - name = smprint("%s(%s)", pn, sect->name); + name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { @@ -539,15 +551,99 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) } s->size = sect->size; s->align = sect->align; + sect->sym = s; + } + + // enter sub-symbols into symbol table. + // symbol 0 is the null symbol. + symbols = malloc(obj->nsymtab * sizeof(symbols[0])); + if(symbols == nil) { + diag("out of memory"); + errorexit(); + } + for(i=1; i<obj->nsymtab; i++) { + if(readsym(obj, i, &sym, 1) < 0) + goto bad; + symbols[i] = sym.sym; + if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) + continue; + if(sym.shndx == ElfSymShnCommon) { + s = sym.sym; + if(s->size < sym.size) + s->size = sym.size; + if(s->type == 0 || s->type == SXREF) + s->type = SBSS; + continue; + } + if(sym.shndx >= obj->nsect || sym.shndx == 0) + continue; + // even when we pass needSym == 1 to readsym, it might still return nil to skip some unwanted symbols + if(sym.sym == S) + continue; + sect = obj->sect+sym.shndx; + if(sect->sym == nil) { + diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); + continue; + } + s = sym.sym; + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } + s->sub = sect->sym->sub; + sect->sym->sub = s; + s->type = sect->sym->type | (s->type&~SMASK) | SSUB; + if(!s->dynexport) { + s->dynimplib = nil; // satisfy dynimport + s->dynimpname = nil; // satisfy dynimport + } + s->value = sym.value; + s->size = sym.size; + s->outer = sect->sym; + if(sect->sym->type == STEXT) { + Prog *p; + + if(s->text != P) { + if(!s->dupok) + diag("%s: duplicate definition of %s", pn, s->name); + } else { + // build a TEXT instruction with a unique pc + // just to make the rest of the linker happy. + p = prg(); + p->as = ATEXT; + p->from.type = D_EXTERN; + p->from.sym = s; + p->textflag = 7; + p->to.type = D_CONST; + p->link = nil; + p->pc = pc++; + s->text = p; + } + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; i<obj->nsect; i++) { + s = obj->sect[i].sym; + if(s == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); if(s->type == STEXT) { if(etextp) etextp->next = s; else textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } - sect->sym = s; - } + } // load relocations for(i=0; i<obj->nsect; i++) { @@ -588,14 +684,24 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) p += 4; } } - if(readsym(obj, info>>32, &sym) < 0) - goto bad; - if(sym.sym == nil) { - werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", - sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); - goto bad; + if((info & 0xffffffff) == 0) { // skip R_*_NONE relocation + j--; + n--; + continue; + } + if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol + rp->sym = S; + } else { + if(readsym(obj, info>>32, &sym, 0) < 0) + goto bad; + sym.sym = symbols[info>>32]; + if(sym.sym == nil) { + werrstr("%s#%d: reloc of invalid sym #%d %s shndx=%d type=%d", + sect->sym->name, j, (int)(info>>32), sym.name, sym.shndx, sym.type); + goto bad; + } + rp->sym = sym.sym; } - rp->sym = sym.sym; rp->type = reltype(pn, (uint32)info, &rp->siz); if(rela) rp->add = add; @@ -615,65 +721,13 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) s->r = r; s->nr = n; } + free(symbols); - // enter sub-symbols into symbol table. - // symbol 0 is the null symbol. - for(i=1; i<obj->nsymtab; i++) { - if(readsym(obj, i, &sym) < 0) - goto bad; - if(sym.type != ElfSymTypeFunc && sym.type != ElfSymTypeObject && sym.type != ElfSymTypeNone) - continue; - if(sym.shndx == ElfSymShnCommon) { - s = sym.sym; - if(s->size < sym.size) - s->size = sym.size; - if(s->type == 0 || s->type == SXREF) - s->type = SBSS; - continue; - } - if(sym.shndx >= obj->nsect || sym.shndx == 0) - continue; - sect = obj->sect+sym.shndx; - if(sect->sym == nil) { - diag("%s: sym#%d: ignoring %s in section %d (type %d)", pn, i, sym.name, sym.shndx, sym.type); - continue; - } - s = sym.sym; - s->sub = sect->sym->sub; - sect->sym->sub = s; - s->type = sect->sym->type | SSUB; - if(!s->dynexport) { - s->dynimplib = nil; // satisfy dynimport - s->dynimpname = nil; // satisfy dynimport - } - s->value = sym.value; - s->size = sym.size; - s->outer = sect->sym; - if(sect->sym->type == STEXT) { - Prog *p; - - if(s->text != P) - diag("%s: duplicate definition of %s", pn, s->name); - // build a TEXT instruction with a unique pc - // just to make the rest of the linker happy. - p = prg(); - p->as = ATEXT; - p->from.type = D_EXTERN; - p->from.sym = s; - p->textflag = 7; - p->to.type = D_CONST; - p->link = nil; - p->pc = pc++; - s->text = p; - - etextp->next = s; - etextp = s; - } - } return; bad: diag("%s: malformed elf file: %r", pn); + free(symbols); } static ElfSect* @@ -707,7 +761,7 @@ map(ElfObj *obj, ElfSect *sect) } static int -readsym(ElfObj *obj, int i, ElfSym *sym) +readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) { Sym *s; @@ -715,6 +769,9 @@ readsym(ElfObj *obj, int i, ElfSym *sym) werrstr("invalid elf symbol index"); return -1; } + if(i == 0) { + diag("readym: read null symbol!"); + } if(obj->is64) { ElfSymBytes64 *b; @@ -743,8 +800,6 @@ readsym(ElfObj *obj, int i, ElfSym *sym) s = nil; if(strcmp(sym->name, "_GLOBAL_OFFSET_TABLE_") == 0) sym->name = ".got"; - if(strcmp(sym->name, "__stack_chk_fail_local") == 0) - sym->other = 0; // rewrite hidden -> default visibility switch(sym->type) { case ElfSymTypeSection: s = obj->sect[sym->shndx].sym; @@ -754,13 +809,37 @@ readsym(ElfObj *obj, int i, ElfSym *sym) case ElfSymTypeNone: switch(sym->bind) { case ElfSymBindGlobal: - if(sym->other != 2) { + if(needSym) { s = lookup(sym->name, 0); - break; + // for global scoped hidden symbols we should insert it into + // symbol hash table, but mark them as hidden. + // __i686.get_pc_thunk.bx is allowed to be duplicated, to + // workaround that we set dupok. + // TODO(minux): correctly handle __i686.get_pc_thunk.bx without + // set dupok generally. See http://codereview.appspot.com/5823055/ + // comment #5 for details. + if(s && sym->other == 2) { + s->type |= SHIDDEN; + s->dupok = 1; + } } - // fall through + break; case ElfSymBindLocal: - s = lookup(sym->name, version); + if(!(thechar == '5' && (strcmp(sym->name, "$a") == 0 || strcmp(sym->name, "$d") == 0))) // binutils for arm generate these mapping symbols, ignore these + if(needSym) { + // local names and hidden visiblity global names are unique + // and should only reference by its index, not name, so we + // don't bother to add them into hash table + s = newsym(sym->name, version); + s->type |= SHIDDEN; + } + break; + case ElfSymBindWeak: + if(needSym) { + s = newsym(sym->name, 0); + if(sym->other == 2) + s->type |= SHIDDEN; + } break; default: werrstr("%s: invalid symbol binding %d", sym->name, sym->bind); @@ -797,6 +876,18 @@ reltype(char *pn, int elftype, uchar *siz) switch(R(thechar, elftype)) { default: diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); + case R('5', R_ARM_ABS32): + case R('5', R_ARM_GOT32): + case R('5', R_ARM_PLT32): + case R('5', R_ARM_GOTOFF): + case R('5', R_ARM_GOTPC): + case R('5', R_ARM_THM_PC22): + case R('5', R_ARM_REL32): + case R('5', R_ARM_CALL): + case R('5', R_ARM_V4BX): + case R('5', R_ARM_GOT_PREL): + case R('5', R_ARM_PC24): + case R('5', R_ARM_JUMP24): case R('6', R_X86_64_PC32): case R('6', R_X86_64_PLT32): case R('6', R_X86_64_GOTPCREL): diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index 388848767..41852f17c 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -440,7 +440,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) Reloc *r, *rp; char *name; - USED(pkg); version++; base = Boffset(f); if(Bread(f, hdr, sizeof hdr) != sizeof hdr) @@ -566,16 +565,21 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) continue; if(strcmp(sect->name, "__eh_frame") == 0) continue; - name = smprint("%s(%s/%s)", pn, sect->segname, sect->name); + name = smprint("%s(%s/%s)", pkg, sect->segname, sect->name); s = lookup(name, version); if(s->type != 0) { werrstr("duplicate %s/%s", sect->segname, sect->name); goto bad; } free(name); - s->p = dat + sect->addr - c->seg.vmaddr; + s->np = sect->size; s->size = s->np; + if((sect->flags & 0xff) == 1) // S_ZEROFILL + s->p = mal(s->size); + else { + s->p = dat + sect->addr - c->seg.vmaddr; + } if(strcmp(sect->segname, "__TEXT") == 0) { if(strcmp(sect->name, "__text") == 0) @@ -589,13 +593,6 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) } else s->type = SDATA; } - if(s->type == STEXT) { - if(etextp) - etextp->next = s; - else - textp = s; - etextp = s; - } sect->sym = s; } @@ -627,6 +624,12 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) werrstr("reference to invalid section %s/%s", sect->segname, sect->name); continue; } + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } s->type = outer->type | SSUB; s->sub = outer->sub; outer->sub = s; @@ -657,11 +660,29 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) p->link = nil; p->pc = pc++; s->text = p; + } + sym->sym = s; + } - etextp->next = s; + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; i<c->seg.nsect; i++) { + sect = &c->seg.sect[i]; + if((s = sect->sym) == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } - sym->sym = s; } // load relocations @@ -680,19 +701,28 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) int k; MachoSect *ks; - if(thechar != '8') + if(thechar != '8') { + // mach-o only uses scattered relocation on 32-bit platforms diag("unexpected scattered relocation"); + continue; + } - // on 386, rewrite scattered 4/1 relocation into - // the pseudo-pc-relative reference that it is. + // on 386, rewrite scattered 4/1 relocation and some + // scattered 2/1 relocation into the pseudo-pc-relative + // reference that it is. // assume that the second in the pair is in this section // and use that as the pc-relative base. - if(thechar != '8' || rel->type != 4 || j+1 >= sect->nreloc || - !(rel+1)->scattered || (rel+1)->type != 1 || - (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { + if(j+1 >= sect->nreloc) { + werrstr("unsupported scattered relocation %d", (int)rel->type); + goto bad; + } + if(!(rel+1)->scattered || (rel+1)->type != 1 || + (rel->type != 4 && rel->type != 2) || + (rel+1)->value < sect->addr || (rel+1)->value >= sect->addr+sect->size) { werrstr("unsupported scattered relocation %d/%d", (int)rel->type, (int)(rel+1)->type); goto bad; } + rp->siz = rel->length; rp->off = rel->addr; diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index feb8620bd..39c15e6a1 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -145,7 +145,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) PeSym *sym; USED(len); - USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); @@ -213,7 +212,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) if(map(obj, sect) < 0) goto bad; - name = smprint("%s(%s)", pn, sect->name); + name = smprint("%s(%s)", pkg, sect->name); s = lookup(name, version); free(name); switch(sect->sh.Characteristics&(IMAGE_SCN_CNT_UNINITIALIZED_DATA|IMAGE_SCN_CNT_INITIALIZED_DATA| @@ -237,13 +236,6 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) s->p = sect->base; s->np = sect->size; s->size = sect->size; - if(s->type == STEXT) { - if(etextp) - etextp->next = s; - else - textp = s; - etextp = s; - } sect->sym = s; if(strcmp(sect->name, ".rsrc") == 0) setpersrc(sect->sym); @@ -300,6 +292,11 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) rp->add = le64(rsect->base+rp->off); break; } + // ld -r could generate multiple section symbols for the + // same section but with different values, we have to take + // that into account + if (obj->pesym[symindex].name[0] == '.') + rp->add += obj->pesym[symindex].value; } qsort(r, rsect->sh.NumberOfRelocations, sizeof r[0], rbyoff); @@ -341,6 +338,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) if(sect == nil) return; + + if(s->outer != S) { + if(s->dupok) + continue; + diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); + errorexit(); + } s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | SSUB; @@ -363,9 +367,27 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) p->link = nil; p->pc = pc++; s->text = p; - - etextp->next = s; + } + } + + // Sort outer lists by address, adding to textp. + // This keeps textp in increasing address order. + for(i=0; i<obj->nsect; i++) { + s = obj->sect[i].sym; + if(s == S) + continue; + if(s->sub) + s->sub = listsort(s->sub, valuecmp, offsetof(Sym, sub)); + if(s->type == STEXT) { + if(etextp) + etextp->next = s; + else + textp = s; etextp = s; + for(s = s->sub; s != S; s = s->sub) { + etextp->next = s; + etextp = s; + } } } diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 4a100cac3..26fa4f2ac 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -72,6 +72,8 @@ Lflag(char *arg) void libinit(void) { + char *race; + fmtinstall('i', iconv); fmtinstall('Y', Yconv); fmtinstall('Z', Zconv); @@ -80,7 +82,10 @@ libinit(void) print("goarch is not known: %s\n", goarch); // add goroot to the end of the libdir list. - Lflag(smprint("%s/pkg/%s_%s", goroot, goos, goarch)); + race = ""; + if(flag_race) + race = "_race"; + Lflag(smprint("%s/pkg/%s_%s%s", goroot, goos, goarch, race)); // Unix doesn't like it when we write to a running (or, sometimes, // recently run) binary, so remove the output file before writing it. @@ -99,6 +104,13 @@ libinit(void) sprint(INITENTRY, "_rt0_%s_%s", goarch, goos); } lookup(INITENTRY, 0)->type = SXREF; + if(flag_shared) { + if(LIBINITENTRY == nil) { + LIBINITENTRY = mal(strlen(goarch)+strlen(goos)+20); + sprint(LIBINITENTRY, "_rt0_%s_%s_lib", goarch, goos); + } + lookup(LIBINITENTRY, 0)->type = SXREF; + } } void @@ -281,6 +293,8 @@ loadlib(void) loadinternal("runtime"); if(thechar == '5') loadinternal("math"); + if(flag_race) + loadinternal("runtime/race"); for(i=0; i<libraryp; i++) { if(debug['v']) @@ -298,10 +312,11 @@ loadlib(void) // // Exception: on OS X, programs such as Shark only work with dynamic // binaries, so leave it enabled on OS X (Mach-O) binaries. - if(!havedynamic && HEADTYPE != Hdarwin) + if(!flag_shared && !havedynamic && HEADTYPE != Hdarwin) debug['d'] = 1; importcycles(); + sortdynexp(); } /* @@ -366,7 +381,7 @@ objfile(char *file, char *pkg) return; } - /* skip over __.SYMDEF */ + /* skip over __.GOSYMDEF */ off = Boffset(f); if((l = nextar(f, off, &arhdr)) <= 0) { diag("%s: short read on archive file symbol header", file); @@ -402,7 +417,7 @@ objfile(char *file, char *pkg) * the individual symbols that are unused. * * loading every object will also make it possible to - * load foreign objects not referenced by __.SYMDEF. + * load foreign objects not referenced by __.GOSYMDEF. */ for(;;) { l = nextar(f, off, &arhdr); @@ -548,6 +563,36 @@ eof: free(pn); } +Sym* +newsym(char *symb, int v) +{ + Sym *s; + int l; + + l = strlen(symb) + 1; + s = mal(sizeof(*s)); + if(debug['v'] > 1) + Bprint(&bso, "newsym %s\n", symb); + + s->dynid = -1; + s->plt = -1; + s->got = -1; + s->name = mal(l + 1); + memmove(s->name, symb, l); + + s->type = 0; + s->version = v; + s->value = 0; + s->sig = 0; + s->size = 0; + nsymbol++; + + s->allsym = allsym; + allsym = s; + + return s; +} + static Sym* _lookup(char *symb, int v, int creat) { @@ -569,27 +614,10 @@ _lookup(char *symb, int v, int creat) if(!creat) return nil; - s = mal(sizeof(*s)); - if(debug['v'] > 1) - Bprint(&bso, "lookup %s\n", symb); - - s->dynid = -1; - s->plt = -1; - s->got = -1; - s->name = mal(l + 1); - memmove(s->name, symb, l); - + s = newsym(symb, v); s->hash = hash[h]; - s->type = 0; - s->version = v; - s->value = 0; - s->sig = 0; - s->size = 0; hash[h] = s; - nsymbol++; - s->allsym = allsym; - allsym = s; return s; } @@ -1372,6 +1400,19 @@ headtype(char *name) return -1; // not reached } +char* +headstr(int v) +{ + static char buf[20]; + int i; + + for(i=0; headers[i].name; i++) + if(v == headers[i].val) + return headers[i].name; + snprint(buf, sizeof buf, "%d", v); + return buf; +} + void undef(void) { @@ -1419,6 +1460,23 @@ Yconv(Fmt *fp) vlong coutpos; +static void +dowrite(int fd, char *p, int n) +{ + int m; + + while(n > 0) { + m = write(fd, p, n); + if(m <= 0) { + cursym = S; + diag("write error: %r"); + errorexit(); + } + n -= m; + p += m; + } +} + void cflush(void) { @@ -1427,13 +1485,8 @@ cflush(void) if(cbpmax < cbp) cbpmax = cbp; n = cbpmax - buf.cbuf; - if(n) { - if(write(cout, buf.cbuf, n) != n) { - diag("write error: %r"); - errorexit(); - } - coutpos += n; - } + dowrite(cout, buf.cbuf, n); + coutpos += n; cbp = buf.cbuf; cbc = sizeof(buf.cbuf); cbpmax = cbp; @@ -1472,9 +1525,142 @@ void cwrite(void *buf, int n) { cflush(); - if(write(cout, buf, n) != n) { - diag("write error: %r"); - errorexit(); - } + if(n <= 0) + return; + dowrite(cout, buf, n); coutpos += n; } + +void +usage(void) +{ + fprint(2, "usage: %cl [options] main.%c\n", thechar, thechar); + flagprint(2); + exits("usage"); +} + +void +setheadtype(char *s) +{ + HEADTYPE = headtype(s); +} + +void +setinterp(char *s) +{ + debug['I'] = 1; // denote cmdline interpreter override + interpreter = s; +} + +void +doversion(void) +{ + print("%cl version %s\n", thechar, getgoversion()); + errorexit(); +} + +void +genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) +{ + Auto *a; + Sym *s; + int32 off; + + // These symbols won't show up in the first loop below because we + // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. + s = lookup("text", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + + for(s=allsym; s!=S; s=s->allsym) { + if(s->hide) + continue; + switch(s->type&SMASK) { + case SCONST: + case SRODATA: + case SSYMTAB: + case SPCLNTAB: + case SDATA: + case SNOPTRDATA: + case SELFROSECT: + case SMACHOGOT: + case STYPE: + case SSTRING: + case SGOSTRING: + case SWINDOWS: + case SGCDATA: + case SGCBSS: + if(!s->reachable) + continue; + put(s, s->name, 'D', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SBSS: + case SNOPTRBSS: + if(!s->reachable) + continue; + if(s->np > 0) + diag("%s should not be bss (size=%d type=%d special=%d)", s->name, (int)s->np, s->type, s->special); + put(s, s->name, 'B', symaddr(s), s->size, s->version, s->gotype); + continue; + + case SFILE: + put(nil, s->name, 'f', s->value, 0, s->version, 0); + continue; + } + } + + for(s = textp; s != nil; s = s->next) { + if(s->text == nil) + continue; + + /* filenames first */ + for(a=s->autom; a; a=a->link) + if(a->type == D_FILE) + put(nil, a->asym->name, 'z', a->aoffset, 0, 0, 0); + else + if(a->type == D_FILE1) + put(nil, a->asym->name, 'Z', a->aoffset, 0, 0, 0); + + put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); + + /* frame, locals, args, auto and param after */ + put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0); + put(nil, ".locals", 'm', s->locals, 0, 0, 0); + put(nil, ".args", 'm', s->args, 0, 0, 0); + + for(a=s->autom; a; a=a->link) { + // Emit a or p according to actual offset, even if label is wrong. + // This avoids negative offsets, which cannot be encoded. + if(a->type != D_AUTO && a->type != D_PARAM) + continue; + + // compute offset relative to FP + if(a->type == D_PARAM) + off = a->aoffset; + else + off = a->aoffset - PtrSize; + + // FP + if(off >= 0) { + put(nil, a->asym->name, 'p', off, 0, 0, a->gotype); + continue; + } + + // SP + if(off <= -PtrSize) { + put(nil, a->asym->name, 'a', -(off+PtrSize), 0, 0, a->gotype); + continue; + } + + // Otherwise, off is addressing the saved program counter. + // Something underhanded is going on. Say nothing. + } + } + if(debug['v'] || debug['n']) + Bprint(&bso, "symsize = %ud\n", symsize); + Bflush(&bso); +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 02dac6e1c..94ad76ecc 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -31,7 +31,7 @@ enum { Sxxx, - + /* order here is order in output file */ STEXT, SMACHOPLT, @@ -39,11 +39,15 @@ enum SSTRING, SGOSTRING, SRODATA, + STYPELINK, + SGCDATA, + SGCBSS, SSYMTAB, SPCLNTAB, SELFROSECT, SELFSECT, SNOPTRDATA, + SDATARELRO, SDATA, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, @@ -59,9 +63,12 @@ enum SFILE, SCONST, SDYNIMPORT, + SHOSTOBJ, SSUB = 1<<8, /* sub-symbol, linked from parent via ->sub list */ - + SMASK = SSUB - 1, + SHIDDEN = 1<<9, // hidden or local symbol + NHASH = 100003, }; @@ -92,6 +99,8 @@ struct Segment Section* sect; }; +#pragma incomplete struct Elf64_Shdr + struct Section { uchar rwx; @@ -100,6 +109,9 @@ struct Section uvlong len; Section *next; // in segment list Segment *seg; + struct Elf64_Shdr *elfsect; + uvlong reloff; + uvlong rellen; }; extern char symname[]; @@ -128,6 +140,12 @@ EXTERN char* thestring; EXTERN int ndynexp; EXTERN int havedynamic; EXTERN int iscgo; +EXTERN int isobj; +EXTERN int elfglobalsymndx; +EXTERN int flag_race; +EXTERN int flag_shared; +EXTERN char* tracksym; +EXTERN char* interpreter; EXTERN Segment segtext; EXTERN Segment segdata; @@ -142,6 +160,7 @@ void addhist(int32 line, int type); void asmlc(void); void histtoauto(void); void collapsefrog(Sym *s); +Sym* newsym(char *symb, int v); Sym* lookup(char *symb, int v); Sym* rlookup(char *symb, int v); void nuxiinit(void); @@ -163,6 +182,8 @@ void symtab(void); void Lflag(char *arg); void usage(void); void adddynrel(Sym*, Reloc*); +void adddynrela(Sym*, Sym*, Reloc*); +Sym* lookuprel(void); void ldobj1(Biobuf *f, char*, int64 len, char *pn); void ldobj(Biobuf*, char*, int64, char*, int); void ldelf(Biobuf*, char*, int64, char*); @@ -176,21 +197,26 @@ void deadcode(void); Reloc* addrel(Sym*); void codeblk(int32, int32); void datblk(int32, int32); -Sym* datsort(Sym*); void reloc(void); void relocsym(Sym*); void savedata(Sym*, Prog*, char*); void symgrow(Sym*, int32); void addstrdata(char*, char*); vlong addstring(Sym*, char*); +vlong adduint8(Sym*, uint8); +vlong adduint16(Sym*, uint16); vlong adduint32(Sym*, uint32); vlong adduint64(Sym*, uint64); vlong addaddr(Sym*, Sym*); vlong addaddrplus(Sym*, Sym*, int32); vlong addpcrelplus(Sym*, Sym*, int32); vlong addsize(Sym*, Sym*); -vlong adduint8(Sym*, uint8); -vlong adduint16(Sym*, uint16); +vlong setaddrplus(Sym*, vlong, Sym*, int32); +vlong setaddr(Sym*, vlong, Sym*); +void setuint8(Sym*, vlong, uint8); +void setuint16(Sym*, vlong, uint16); +void setuint32(Sym*, vlong, uint32); +void setuint64(Sym*, vlong, uint64); void asmsym(void); void asmelfsym(void); void asmplan9sym(void); @@ -209,6 +235,11 @@ void dostkcheck(void); void undef(void); void doweak(void); void setpersrc(Sym*); +void doversion(void); +void usage(void); +void setinterp(char*); +Sym* listsort(Sym*, int(*cmp)(Sym*, Sym*), int); +int valuecmp(Sym*, Sym*); int pathchar(void); void* mal(uint32); @@ -233,12 +264,6 @@ struct Endian extern Endian be, le; -// relocation size bits -enum { - Rbig = 128, - Rlittle = 64, -}; - /* set by call to mywhatsys() */ extern char* goroot; extern char* goarch; @@ -282,6 +307,8 @@ EXTERN char* headstring; extern Header headers[]; int headtype(char*); +char* headstr(int); +void setheadtype(char*); int Yconv(Fmt*); @@ -312,3 +339,25 @@ void cseek(vlong); void cwrite(void*, int); void importcycles(void); int Zconv(Fmt*); + +uint8 decodetype_kind(Sym*); +vlong decodetype_size(Sym*); +Sym* decodetype_gc(Sym*); +Sym* decodetype_arrayelem(Sym*); +vlong decodetype_arraylen(Sym*); +Sym* decodetype_ptrelem(Sym*); +Sym* decodetype_mapkey(Sym*); +Sym* decodetype_mapvalue(Sym*); +Sym* decodetype_chanelem(Sym*); +int decodetype_funcdotdotdot(Sym*); +int decodetype_funcincount(Sym*); +int decodetype_funcoutcount(Sym*); +Sym* decodetype_funcintype(Sym*, int); +Sym* decodetype_funcouttype(Sym*, int); +int decodetype_structfieldcount(Sym*); +char* decodetype_structfieldname(Sym*, int); +Sym* decodetype_structfieldtype(Sym*, int); +vlong decodetype_structfieldoffs(Sym*, int); +vlong decodetype_ifacemethodcount(Sym*); + +void sortdynexp(void); diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index f55104150..baea6ff03 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -50,7 +50,7 @@ struct MachoDebug { uint32 filesize; }; -MachoHdr* getMachoHdr(); +MachoHdr* getMachoHdr(void); MachoSeg* newMachoSeg(char*, int); MachoSect* newMachoSect(MachoSeg*, char*); MachoLoad* newMachoLoad(uint32, uint32); diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index 1d70b4808..f2903ba0f 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -148,6 +148,9 @@ peinit(void) PESECTHEADR = rnd(PEFILEHEADR, PESECTALIGN); nextsectoff = PESECTHEADR; nextfileoff = PEFILEHEADR; + + // some mingw libs depend on this symbol, for example, FindPESectionByName + xdefine("__image_base__", SDATA, PEBASE); } static void @@ -533,6 +536,7 @@ addexcept(IMAGE_SECTION_HEADER *text) uvlong n; Sym *sym; + USED(text); if(thechar != '6') return; @@ -656,7 +660,7 @@ asmbpe(void) // for other threads we specify stack size in runtime explicitly // (runtime knows whether cgo is enabled or not). // If you change stack reserve sizes here, - // change them in runtime/cgo/windows_386/amd64.c as well. + // change STACKSIZE in runtime/cgo/gcc_windows_{386,amd64}.c as well. if(!iscgo) { set(SizeOfStackReserve, 0x00010000); set(SizeOfStackCommit, 0x0000ffff); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 129b13ea0..89a594872 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -36,7 +36,7 @@ static int maxelfstr; -int +static int putelfstr(char *s) { int off, n; @@ -57,14 +57,14 @@ putelfstr(char *s) return off; } -void -putelfsyment(int off, vlong addr, vlong size, int info, int shndx) +static void +putelfsyment(int off, vlong addr, vlong size, int info, int shndx, int other) { switch(thechar) { case '6': LPUT(off); cput(info); - cput(0); + cput(other); WPUT(shndx); VPUT(addr); VPUT(size); @@ -75,17 +75,21 @@ putelfsyment(int off, vlong addr, vlong size, int info, int shndx) LPUT(addr); LPUT(size); cput(info); - cput(0); + cput(other); WPUT(shndx); symsize += ELF32SYMSIZE; break; } } -void +static int numelfsym = 1; // 0 is reserved +static int elfbind; + +static void putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - int bind, type, shndx, off; + int bind, type, off; + Sym *xo; USED(go); switch(t) { @@ -93,37 +97,68 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) return; case 'T': type = STT_FUNC; - shndx = elftextsh + 0; break; case 'D': type = STT_OBJECT; - if((x->type&~SSUB) == SRODATA) - shndx = elftextsh + 1; - else - shndx = elftextsh + 2; break; case 'B': type = STT_OBJECT; - shndx = elftextsh + 3; break; } - bind = ver ? STB_LOCAL : STB_GLOBAL; + xo = x; + while(xo->outer != nil) + xo = xo->outer; + if(xo->sect == nil) { + cursym = x; + diag("missing section in putelfsym"); + return; + } + if(xo->sect->elfsect == nil) { + cursym = x; + diag("missing ELF section in putelfsym"); + return; + } + + // One pass for each binding: STB_LOCAL, STB_GLOBAL, + // maybe one day STB_WEAK. + bind = (ver || (x->type & SHIDDEN)) ? STB_LOCAL : STB_GLOBAL; + if(bind != elfbind) + return; + off = putelfstr(s); - putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx); + if(isobj) + addr -= xo->sect->vaddr; + putelfsyment(off, addr, size, (bind<<4)|(type&0xf), xo->sect->elfsect->shnum, (x->type & SHIDDEN) ? 2 : 0); + x->elfsym = numelfsym++; } void asmelfsym(void) { + Sym *s; + // the first symbol entry is reserved - putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0); + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0, 0); + + elfbind = STB_LOCAL; genasmsym(putelfsym); + + elfbind = STB_GLOBAL; + elfglobalsymndx = numelfsym; + genasmsym(putelfsym); + + for(s=allsym; s!=S; s=s->allsym) { + if(s->type != SHOSTOBJ) + continue; + putelfsyment(putelfstr(s->name), 0, 0, (STB_GLOBAL<<4)|STT_NOTYPE, 0, 0); + s->elfsym = numelfsym++; + } } -void +static void putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - int i; + int i, l; USED(go); USED(ver); @@ -142,6 +177,11 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) case 'z': case 'Z': case 'm': + l = 4; + if(HEADTYPE == Hplan9x64 && !debug['8']) { + lputb(addr>>32); + l = 8; + } lputb(addr); cput(t+0x80); /* 0x80 is variable length */ @@ -162,7 +202,7 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) cput(s[i]); cput(0); } - symsize += 4 + 1 + i + 1; + symsize += l + 1 + i + 1; break; default: return; @@ -192,7 +232,7 @@ static void slputb(int32 v) { uchar *p; - + symgrow(symt, symt->size+4); p = symt->p + symt->size; *p++ = v>>24; @@ -202,6 +242,22 @@ slputb(int32 v) symt->size += 4; } +static void +slputl(int32 v) +{ + uchar *p; + + symgrow(symt, symt->size+4); + p = symt->p + symt->size; + *p++ = v; + *p++ = v>>8; + *p++ = v>>16; + *p = v>>24; + symt->size += 4; +} + +static void (*slput)(int32); + void wputl(ushort w) { @@ -248,32 +304,76 @@ vputl(uint64 v) lputl(v >> 32); } +// Emit symbol table entry. +// The table format is described at the top of ../../pkg/runtime/symtab.c. void putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) { - int i, f, l; + int i, f, c; + vlong v1; Reloc *rel; USED(size); - if(t == 'f') - name++; - l = 4; -// if(!debug['8']) -// l = 8; + + // type byte + if('A' <= t && t <= 'Z') + c = t - 'A'; + else if('a' <= t && t <= 'z') + c = t - 'a' + 26; + else { + diag("invalid symbol table type %c", t); + errorexit(); + return; + } + + if(s != nil) + c |= 0x40; // wide value + if(typ != nil) + c |= 0x80; // has go type + scput(c); + + // value if(s != nil) { + // full width rel = addrel(symt); - rel->siz = l + Rbig; + rel->siz = PtrSize; rel->sym = s; rel->type = D_ADDR; rel->off = symt->size; - v = 0; - } - if(l == 8) - slputb(v>>32); - slputb(v); - if(ver) - t += 'a' - 'A'; - scput(t+0x80); /* 0x80 is variable length */ + if(PtrSize == 8) + slput(0); + slput(0); + } else { + // varint + if(v < 0) { + diag("negative value in symbol table: %s %lld", name, v); + errorexit(); + } + v1 = v; + while(v1 >= 0x80) { + scput(v1 | 0x80); + v1 >>= 7; + } + scput(v1); + } + + // go type if present + if(typ != nil) { + if(!typ->reachable) + diag("unreachable type %s", typ->name); + rel = addrel(symt); + rel->siz = PtrSize; + rel->sym = typ; + rel->type = D_ADDR; + rel->off = symt->size; + if(PtrSize == 8) + slput(0); + slput(0); + } + + // name + if(t == 'f') + name++; if(t == 'Z' || t == 'z') { scput(name[0]); @@ -283,24 +383,11 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) } scput(0); scput(0); - } - else { + } else { for(i=0; name[i]; i++) scput(name[i]); scput(0); } - if(typ) { - if(!typ->reachable) - diag("unreachable type %s", typ->name); - rel = addrel(symt); - rel->siz = l; - rel->sym = typ; - rel->type = D_ADDR; - rel->off = symt->size; - } - if(l == 8) - slputb(0); - slputb(0); if(debug['n']) { if(t == 'z' || t == 'Z') { @@ -313,25 +400,34 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) return; } if(ver) - Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s->name, ver, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, name, ver, typ ? typ->name : ""); else - Bprint(&bso, "%c %.8llux %s %s\n", t, v, s->name, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s %s\n", t, v, name, typ ? typ->name : ""); } } void symtab(void) { - Sym *s; - + Sym *s, *symtype, *symtypelink, *symgostring; dosymtype(); // Define these so that they'll get put into the symbol table. // data.c:/^address will provide the actual values. xdefine("text", STEXT, 0); xdefine("etext", STEXT, 0); + xdefine("typelink", SRODATA, 0); + xdefine("etypelink", SRODATA, 0); xdefine("rodata", SRODATA, 0); xdefine("erodata", SRODATA, 0); + if(flag_shared) { + xdefine("datarelro", SDATARELRO, 0); + xdefine("edatarelro", SDATARELRO, 0); + } + xdefine("gcdata", SGCDATA, 0); + xdefine("egcdata", SGCDATA, 0); + xdefine("gcbss", SGCBSS, 0); + xdefine("egcbss", SGCBSS, 0); xdefine("noptrdata", SNOPTRDATA, 0); xdefine("enoptrdata", SNOPTRDATA, 0); xdefine("data", SDATA, 0); @@ -343,23 +439,27 @@ symtab(void) xdefine("end", SBSS, 0); xdefine("epclntab", SRODATA, 0); xdefine("esymtab", SRODATA, 0); - + // pseudo-symbols to mark locations of type, string, and go string data. s = lookup("type.*", 0); s->type = STYPE; s->size = 0; s->reachable = 1; + symtype = s; s = lookup("go.string.*", 0); s->type = SGOSTRING; s->size = 0; s->reachable = 1; + symgostring = s; + + symtypelink = lookup("typelink", 0); symt = lookup("symtab", 0); symt->type = SSYMTAB; symt->size = 0; symt->reachable = 1; - + // assign specific types so that they sort together. // within a type they sort by size, so the .* symbols // just defined above will be first. @@ -370,14 +470,44 @@ symtab(void) if(strncmp(s->name, "type.", 5) == 0) { s->type = STYPE; s->hide = 1; + s->outer = symtype; + } + if(strncmp(s->name, "go.typelink.", 12) == 0) { + s->type = STYPELINK; + s->hide = 1; + s->outer = symtypelink; } if(strncmp(s->name, "go.string.", 10) == 0) { s->type = SGOSTRING; s->hide = 1; + s->outer = symgostring; } } if(debug['s']) return; + + switch(thechar) { + default: + diag("unknown architecture %c", thechar); + errorexit(); + case '5': + case '6': + case '8': + // little-endian symbol table + slput = slputl; + break; + case 'v': + // big-endian symbol table + slput = slputb; + break; + } + // new symbol table header. + slput(0xfffffffd); + scput(0); + scput(0); + scput(0); + scput(PtrSize); + genasmsym(putsymb); } diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go index c84369a5f..480c1c3dd 100644 --- a/src/cmd/nm/doc.go +++ b/src/cmd/nm/doc.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. +// +build ignore + /* Nm is a version of the Plan 9 nm command. The original is documented at @@ -11,11 +13,11 @@ Nm is a version of the Plan 9 nm command. The original is documented at It prints the name list (symbol table) for programs compiled by gc as well as the Plan 9 C compiler. -This implementation adds the flag -S, which prints each symbol's size +This implementation adds the flag -S, which prints each symbol's size in decimal after its address. Usage: go tool nm [-aghnsTu] file */ -package documentation +package main diff --git a/src/cmd/nm/nm.c b/src/cmd/nm/nm.c index 845b6c773..668239035 100644 --- a/src/cmd/nm/nm.c +++ b/src/cmd/nm/nm.c @@ -43,7 +43,7 @@ enum{ char *errs; /* exit status */ char *filename; /* current file */ -char symname[]="__.SYMDEF"; /* table of contents file name */ +char symname[]="__.GOSYMDEF"; /* table of contents file name */ int multifile; /* processing multiple files */ int aflag; int gflag; diff --git a/src/cmd/pack/ar.c b/src/cmd/pack/ar.c index 7e07fbc89..7053f841f 100644 --- a/src/cmd/pack/ar.c +++ b/src/cmd/pack/ar.c @@ -77,9 +77,7 @@ typedef struct Armember /* Temp file entry - one per archive member */ typedef struct Arfile /* Temp file control block - one per tempfile */ { - int paged; /* set when some data paged to disk */ char *fname; /* paging file name */ - int fd; /* paging file descriptor */ vlong size; Armember *head; /* head of member chain */ Armember *tail; /* tail of member chain */ @@ -113,7 +111,7 @@ char *opt = "uvnbailogS"; char artemp[] = "/tmp/vXXXXX"; char movtemp[] = "/tmp/v1XXXXX"; char tailtemp[] = "/tmp/v2XXXXX"; -char symdef[] = "__.SYMDEF"; +char symdef[] = "__.GOSYMDEF"; char pkgdef[] = "__.PKGDEF"; int aflag; /* command line flags */ @@ -159,7 +157,6 @@ int bamatch(char*, char*); int duplicate(char*, char**); Armember *getdir(Biobuf*); void getpkgdef(char**, int*); -int getspace(void); void install(char*, Arfile*, Arfile*, Arfile*, int); void loadpkgdata(char*, int); void longt(Armember*); @@ -169,7 +166,6 @@ Arfile *newtempfile(char*); Armember *newmember(void); void objsym(Sym*, void*); int openar(char*, int, int); -int page(Arfile*); void pmode(long); void rl(int); void scanobj(Biobuf*, Arfile*, long); @@ -1534,24 +1530,8 @@ void arstream(int fd, Arfile *ap) { Armember *bp; - int i; - char buf[8192]; - - if (ap->paged) { /* copy from disk */ - seek(ap->fd, 0, 0); - for (;;) { - i = read(ap->fd, buf, sizeof(buf)); - if (i < 0) - rderr(); - if (i == 0) - break; - if (write(fd, buf, i) != i) - wrerr(); - } - close(ap->fd); - ap->paged = 0; - } - /* dump the in-core buffers */ + + /* dump the in-core buffers */ for (bp = ap->head; bp; bp = bp->next) { if (!arwrite(fd, bp)) wrerr(); @@ -1576,35 +1556,6 @@ arwrite(int fd, Armember *bp) return 1; } -/* - * Spill a member to a disk copy of a temp file - */ -int -page(Arfile *ap) -{ - USED(ap); - - sysfatal("page"); - return 1; -} - -/* - * try to reclaim space by paging. we try to spill the start, middle, - * and end files, in that order. there is no particular reason for the - * ordering. - */ -int -getspace(void) -{ - if (astart && astart->head && page(astart)) - return 1; - if (amiddle && amiddle->head && page(amiddle)) - return 1; - if (aend && aend->head && page(aend)) - return 1; - return 0; -} - void arfree(Arfile *ap) /* free a member buffer */ { @@ -1633,13 +1584,11 @@ armalloc(int n) if(n&1) n++; - do { - cp = malloc(n); - if (cp) { - memset(cp, 0, n); - return cp; - } - } while (getspace()); + cp = malloc(n); + if (cp) { + memset(cp, 0, n); + return cp; + } fprint(2, "pack: out of memory\n"); exits("malloc"); return 0; diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go index 8b17f3ca2..67b789731 100644 --- a/src/cmd/pack/doc.go +++ b/src/cmd/pack/doc.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. +// +build ignore + /* Pack is a variant of the Plan 9 ar tool. The original is documented at @@ -24,4 +26,4 @@ The new option 'P' causes pack to remove the given prefix from file names in the line number information in object files that are already stored in or added to the archive. */ -package documentation +package main diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile new file mode 100644 index 000000000..3f528d751 --- /dev/null +++ b/src/cmd/prof/Makefile @@ -0,0 +1,5 @@ +# 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. + +include ../../Make.dist diff --git a/src/cmd/prof/doc.go b/src/cmd/prof/doc.go new file mode 100644 index 000000000..2640167d3 --- /dev/null +++ b/src/cmd/prof/doc.go @@ -0,0 +1,49 @@ +// 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. + +// +build ignore + +/* + +Prof is a rudimentary real-time profiler. + +Given a command to run or the process id (pid) of a command already +running, it samples the program's state at regular intervals and reports +on its behavior. With no options, it prints a histogram of the locations +in the code that were sampled during execution. + +Since it is a real-time profiler, unlike a traditional profiler it samples +the program's state even when it is not running, such as when it is +asleep or waiting for I/O. Each thread contributes equally to the +statistics. + +Usage: + go tool prof -p pid [-t total_secs] [-d delta_msec] [6.out args ...] + +The output modes (default -h) are: + + -P file.prof: + Write the profile information to file.prof, in the format used by pprof. + At the moment, this only works on Linux amd64 binaries and requires that the + binary be written using 6l -e to produce ELF debug info. + See http://code.google.com/p/google-perftools for details. + -h: histograms + How many times a sample occurred at each location. + -f: dynamic functions + At each sample period, print the name of the executing function. + -l: dynamic file and line numbers + At each sample period, print the file and line number of the executing instruction. + -r: dynamic registers + At each sample period, print the register contents. + -s: dynamic function stack traces + At each sample period, print the symbolic stack trace. + +Flag -t sets the maximum real time to sample, in seconds, and -d +sets the sampling interval in milliseconds. The default is to sample +every 100ms until the program completes. + +It is installed as go tool prof and is architecture-independent. + +*/ +package main diff --git a/src/cmd/prof/main.c b/src/cmd/prof/main.c new file mode 100644 index 000000000..6c591ba18 --- /dev/null +++ b/src/cmd/prof/main.c @@ -0,0 +1,910 @@ +// 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. + +// +build !plan9 + +#include <u.h> +#include <time.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> + +#define Ureg Ureg_amd64 + #include <ureg_amd64.h> +#undef Ureg +#define Ureg Ureg_x86 + #include <ureg_x86.h> +#undef Ureg +#include <mach.h> + +char* file = "6.out"; +static Fhdr fhdr; +int have_syms; +int fd; +struct Ureg_amd64 ureg_amd64; +struct Ureg_x86 ureg_x86; +int total_sec = 0; +int delta_msec = 100; +int nsample; +int nsamplethread; + +// pprof data, stored as sequences of N followed by N PC values. +// See http://code.google.com/p/google-perftools . +uvlong *ppdata; // traces +Biobuf* pproffd; // file descriptor to write trace info +long ppstart; // start position of current trace +long nppdata; // length of data +long ppalloc; // size of allocated data +char ppmapdata[10*1024]; // the map information for the output file + +// output formats +int pprof; // print pprof output to named file +int functions; // print functions +int histograms; // print histograms +int linenums; // print file and line numbers rather than function names +int registers; // print registers +int stacks; // print stack traces + +int pid; // main process pid + +int nthread; // number of threads +int thread[32]; // thread pids +Map *map[32]; // thread maps + +void +Usage(void) +{ + fprint(2, "Usage: prof -p pid [-t total_secs] [-d delta_msec]\n"); + fprint(2, " prof [-t total_secs] [-d delta_msec] 6.out args ...\n"); + fprint(2, "\tformats (default -h):\n"); + fprint(2, "\t\t-P file.prof: write [c]pprof output to file.prof\n"); + fprint(2, "\t\t-h: histograms\n"); + fprint(2, "\t\t-f: dynamic functions\n"); + fprint(2, "\t\t-l: dynamic file and line numbers\n"); + fprint(2, "\t\t-r: dynamic registers\n"); + fprint(2, "\t\t-s: dynamic function stack traces\n"); + fprint(2, "\t\t-hs: include stack info in histograms\n"); + exit(2); +} + +typedef struct PC PC; +struct PC { + uvlong pc; + uvlong callerpc; + unsigned int count; + PC* next; +}; + +enum { + Ncounters = 256 +}; + +PC *counters[Ncounters]; + +// Set up by setarch() to make most of the code architecture-independent. +typedef struct Arch Arch; +struct Arch { + char* name; + void (*regprint)(void); + int (*getregs)(Map*); + int (*getPC)(Map*); + int (*getSP)(Map*); + uvlong (*uregPC)(void); + uvlong (*uregSP)(void); + void (*ppword)(uvlong w); +}; + +void +amd64_regprint(void) +{ + fprint(2, "ax\t0x%llux\n", ureg_amd64.ax); + fprint(2, "bx\t0x%llux\n", ureg_amd64.bx); + fprint(2, "cx\t0x%llux\n", ureg_amd64.cx); + fprint(2, "dx\t0x%llux\n", ureg_amd64.dx); + fprint(2, "si\t0x%llux\n", ureg_amd64.si); + fprint(2, "di\t0x%llux\n", ureg_amd64.di); + fprint(2, "bp\t0x%llux\n", ureg_amd64.bp); + fprint(2, "r8\t0x%llux\n", ureg_amd64.r8); + fprint(2, "r9\t0x%llux\n", ureg_amd64.r9); + fprint(2, "r10\t0x%llux\n", ureg_amd64.r10); + fprint(2, "r11\t0x%llux\n", ureg_amd64.r11); + fprint(2, "r12\t0x%llux\n", ureg_amd64.r12); + fprint(2, "r13\t0x%llux\n", ureg_amd64.r13); + fprint(2, "r14\t0x%llux\n", ureg_amd64.r14); + fprint(2, "r15\t0x%llux\n", ureg_amd64.r15); + fprint(2, "ds\t0x%llux\n", ureg_amd64.ds); + fprint(2, "es\t0x%llux\n", ureg_amd64.es); + fprint(2, "fs\t0x%llux\n", ureg_amd64.fs); + fprint(2, "gs\t0x%llux\n", ureg_amd64.gs); + fprint(2, "type\t0x%llux\n", ureg_amd64.type); + fprint(2, "error\t0x%llux\n", ureg_amd64.error); + fprint(2, "pc\t0x%llux\n", ureg_amd64.ip); + fprint(2, "cs\t0x%llux\n", ureg_amd64.cs); + fprint(2, "flags\t0x%llux\n", ureg_amd64.flags); + fprint(2, "sp\t0x%llux\n", ureg_amd64.sp); + fprint(2, "ss\t0x%llux\n", ureg_amd64.ss); +} + +int +amd64_getregs(Map *map) +{ + int i; + union { + uvlong regs[1]; + struct Ureg_amd64 ureg; + } u; + + for(i = 0; i < sizeof ureg_amd64; i+=8) { + if(get8(map, (uvlong)i, &u.regs[i/8]) < 0) + return -1; + } + ureg_amd64 = u.ureg; + return 0; +} + +int +amd64_getPC(Map *map) +{ + uvlong x; + int r; + + r = get8(map, offsetof(struct Ureg_amd64, ip), &x); + ureg_amd64.ip = x; + return r; +} + +int +amd64_getSP(Map *map) +{ + uvlong x; + int r; + + r = get8(map, offsetof(struct Ureg_amd64, sp), &x); + ureg_amd64.sp = x; + return r; +} + +uvlong +amd64_uregPC(void) +{ + return ureg_amd64.ip; +} + +uvlong +amd64_uregSP(void) +{ + return ureg_amd64.sp; +} + +void +amd64_ppword(uvlong w) +{ + uchar buf[8]; + + buf[0] = w; + buf[1] = w >> 8; + buf[2] = w >> 16; + buf[3] = w >> 24; + buf[4] = w >> 32; + buf[5] = w >> 40; + buf[6] = w >> 48; + buf[7] = w >> 56; + Bwrite(pproffd, buf, 8); +} + +void +x86_regprint(void) +{ + fprint(2, "ax\t0x%ux\n", ureg_x86.ax); + fprint(2, "bx\t0x%ux\n", ureg_x86.bx); + fprint(2, "cx\t0x%ux\n", ureg_x86.cx); + fprint(2, "dx\t0x%ux\n", ureg_x86.dx); + fprint(2, "si\t0x%ux\n", ureg_x86.si); + fprint(2, "di\t0x%ux\n", ureg_x86.di); + fprint(2, "bp\t0x%ux\n", ureg_x86.bp); + fprint(2, "ds\t0x%ux\n", ureg_x86.ds); + fprint(2, "es\t0x%ux\n", ureg_x86.es); + fprint(2, "fs\t0x%ux\n", ureg_x86.fs); + fprint(2, "gs\t0x%ux\n", ureg_x86.gs); + fprint(2, "cs\t0x%ux\n", ureg_x86.cs); + fprint(2, "flags\t0x%ux\n", ureg_x86.flags); + fprint(2, "pc\t0x%ux\n", ureg_x86.pc); + fprint(2, "sp\t0x%ux\n", ureg_x86.sp); + fprint(2, "ss\t0x%ux\n", ureg_x86.ss); +} + +int +x86_getregs(Map *map) +{ + int i; + + for(i = 0; i < sizeof ureg_x86; i+=4) { + if(get4(map, (uvlong)i, &((uint32*)&ureg_x86)[i/4]) < 0) + return -1; + } + return 0; +} + +int +x86_getPC(Map* map) +{ + return get4(map, offsetof(struct Ureg_x86, pc), &ureg_x86.pc); +} + +int +x86_getSP(Map* map) +{ + return get4(map, offsetof(struct Ureg_x86, sp), &ureg_x86.sp); +} + +uvlong +x86_uregPC(void) +{ + return (uvlong)ureg_x86.pc; +} + +uvlong +x86_uregSP(void) +{ + return (uvlong)ureg_x86.sp; +} + +void +x86_ppword(uvlong w) +{ + uchar buf[4]; + + buf[0] = w; + buf[1] = w >> 8; + buf[2] = w >> 16; + buf[3] = w >> 24; + Bwrite(pproffd, buf, 4); +} + +Arch archtab[] = { + { + "amd64", + amd64_regprint, + amd64_getregs, + amd64_getPC, + amd64_getSP, + amd64_uregPC, + amd64_uregSP, + amd64_ppword, + }, + { + "386", + x86_regprint, + x86_getregs, + x86_getPC, + x86_getSP, + x86_uregPC, + x86_uregSP, + x86_ppword, + }, + { + nil + } +}; + +Arch *arch; + +int +setarch(void) +{ + int i; + + if(mach != nil) { + for(i = 0; archtab[i].name != nil; i++) { + if (strcmp(mach->name, archtab[i].name) == 0) { + arch = &archtab[i]; + return 0; + } + } + } + return -1; +} + +int +getthreads(void) +{ + int i, j, curn, found; + Map *curmap[nelem(map)]; + int curthread[nelem(map)]; + static int complained = 0; + + curn = procthreadpids(pid, curthread, nelem(curthread)); + if(curn <= 0) + return curn; + + if(curn > nelem(map)) { + if(complained == 0) { + fprint(2, "prof: too many threads; limiting to %d\n", nthread, nelem(map)); + complained = 1; + } + curn = nelem(map); + } + if(curn == nthread && memcmp(thread, curthread, curn*sizeof(*thread)) == 0) + return curn; // no changes + + // Number of threads has changed (might be the init case). + // A bit expensive but rare enough not to bother being clever. + for(i = 0; i < curn; i++) { + found = 0; + for(j = 0; j < nthread; j++) { + if(curthread[i] == thread[j]) { + found = 1; + curmap[i] = map[j]; + map[j] = nil; + break; + } + } + if(found) + continue; + + // map new thread + curmap[i] = attachproc(curthread[i], &fhdr); + if(curmap[i] == nil) { + fprint(2, "prof: can't attach to %d: %r\n", curthread[i]); + return -1; + } + } + + for(j = 0; j < nthread; j++) + if(map[j] != nil) + detachproc(map[j]); + + nthread = curn; + memmove(thread, curthread, nthread*sizeof thread[0]); + memmove(map, curmap, sizeof map); + return nthread; +} + +int +sample(Map *map) +{ + static int n; + + n++; + if(registers) { + if(arch->getregs(map) < 0) + goto bad; + } else { + // we need only two registers + if(arch->getPC(map) < 0) + goto bad; + if(arch->getSP(map) < 0) + goto bad; + } + return 1; +bad: + if(n == 1) + fprint(2, "prof: can't read registers: %r\n"); + return 0; +} + +void +addtohistogram(uvlong pc, uvlong callerpc, uvlong sp) +{ + int h; + PC *x; + + USED(sp); + + h = (pc + callerpc*101) % Ncounters; + for(x = counters[h]; x != NULL; x = x->next) { + if(x->pc == pc && x->callerpc == callerpc) { + x->count++; + return; + } + } + x = malloc(sizeof(PC)); + if(x == nil) + sysfatal("out of memory"); + x->pc = pc; + x->callerpc = callerpc; + x->count = 1; + x->next = counters[h]; + counters[h] = x; +} + +void +addppword(uvlong pc) +{ + if(pc == 0) { + return; + } + if(nppdata == ppalloc) { + ppalloc = (1000+nppdata)*2; + ppdata = realloc(ppdata, ppalloc * sizeof ppdata[0]); + if(ppdata == nil) { + fprint(2, "prof: realloc failed: %r\n"); + exit(2); + } + } + ppdata[nppdata++] = pc; +} + +void +startpptrace(void) +{ + ppstart = nppdata; + addppword(~0); +} + +void +endpptrace(void) +{ + ppdata[ppstart] = nppdata-ppstart-1; +} + +uvlong nextpc; + +void +xptrace(Map *map, uvlong pc, uvlong sp, Symbol *sym) +{ + USED(map); + + char buf[1024]; + if(sym == nil){ + fprint(2, "syms\n"); + return; + } + if(histograms) + addtohistogram(nextpc, pc, sp); + if(!histograms || stacks > 1 || pprof) { + if(nextpc == 0) + nextpc = sym->value; + if(stacks){ + fprint(2, "%s(", sym->name); + fprint(2, ")"); + if(nextpc != sym->value) + fprint(2, "+%#llux ", nextpc - sym->value); + if(have_syms && linenums && fileline(buf, sizeof buf, pc)) { + fprint(2, " %s", buf); + } + fprint(2, "\n"); + } + if (pprof) { + addppword(nextpc); + } + } + nextpc = pc; +} + +void +stacktracepcsp(Map *map, uvlong pc, uvlong sp) +{ + nextpc = pc; + if(pprof){ + startpptrace(); + } + if(machdata->ctrace==nil) + fprint(2, "no machdata->ctrace\n"); + else if(machdata->ctrace(map, pc, sp, 0, xptrace) <= 0) + fprint(2, "no stack frame: pc=%#p sp=%#p\n", pc, sp); + else { + addtohistogram(nextpc, 0, sp); + if(stacks) + fprint(2, "\n"); + } + if(pprof){ + endpptrace(); + } +} + +void +printpc(Map *map, uvlong pc, uvlong sp) +{ + char buf[1024]; + if(registers) + arch->regprint(); + if(have_syms > 0 && linenums && fileline(buf, sizeof buf, pc)) + fprint(2, "%s\n", buf); + if(have_syms > 0 && functions) { + symoff(buf, sizeof(buf), pc, CANY); + fprint(2, "%s\n", buf); + } + if(stacks || pprof){ + stacktracepcsp(map, pc, sp); + } + else if(histograms){ + addtohistogram(pc, 0, sp); + } +} + +void +ppmaps(void) +{ + int fd, n; + char tmp[100]; + Seg *seg; + + // If it's Linux, the info is in /proc/$pid/maps + snprint(tmp, sizeof tmp, "/proc/%d/maps", pid); + fd = open(tmp, 0); + if(fd >= 0) { + n = read(fd, ppmapdata, sizeof ppmapdata - 1); + close(fd); + if(n < 0) { + fprint(2, "prof: can't read %s: %r\n", tmp); + exit(2); + } + ppmapdata[n] = 0; + return; + } + + // It's probably a mac. Synthesize an entry for the text file. + // The register segment may come first but it has a zero offset, so grab the first non-zero offset segment. + for(n = 0; n < 3; n++){ + seg = &map[0]->seg[n]; + if(seg->b == 0) { + continue; + } + snprint(ppmapdata, sizeof ppmapdata, + "%.16x-%.16x r-xp %d 00:00 34968549 %s\n", + seg->b, seg->e, seg->f, "/home/r/6.out" + ); + return; + } + fprint(2, "prof: no text segment in maps for %s\n", file); + exit(2); +} + +void +samples(void) +{ + int i, pid, msec; + struct timespec req; + int getmaps; + + req.tv_sec = delta_msec/1000; + req.tv_nsec = 1000000*(delta_msec % 1000); + getmaps = 0; + if(pprof) + getmaps= 1; + for(msec = 0; total_sec <= 0 || msec < 1000*total_sec; msec += delta_msec) { + nsample++; + nsamplethread += nthread; + for(i = 0; i < nthread; i++) { + pid = thread[i]; + if(ctlproc(pid, "stop") < 0) + return; + if(!sample(map[i])) { + ctlproc(pid, "start"); + return; + } + printpc(map[i], arch->uregPC(), arch->uregSP()); + ctlproc(pid, "start"); + } + nanosleep(&req, NULL); + getthreads(); + if(nthread == 0) + break; + if(getmaps) { + getmaps = 0; + ppmaps(); + } + } +} + +typedef struct Func Func; +struct Func +{ + Func *next; + Symbol s; + uint onstack; + uint leaf; +}; + +Func *func[257]; +int nfunc; + +Func* +findfunc(uvlong pc) +{ + Func *f; + uint h; + Symbol s; + + if(pc == 0) + return nil; + + if(!findsym(pc, CTEXT, &s)) + return nil; + + h = s.value % nelem(func); + for(f = func[h]; f != NULL; f = f->next) + if(f->s.value == s.value) + return f; + + f = malloc(sizeof *f); + if(f == nil) + sysfatal("out of memory"); + memset(f, 0, sizeof *f); + f->s = s; + f->next = func[h]; + func[h] = f; + nfunc++; + return f; +} + +int +compareleaf(const void *va, const void *vb) +{ + Func *a, *b; + + a = *(Func**)va; + b = *(Func**)vb; + if(a->leaf != b->leaf) + return b->leaf - a->leaf; + if(a->onstack != b->onstack) + return b->onstack - a->onstack; + return strcmp(a->s.name, b->s.name); +} + +void +dumphistogram(void) +{ + int i, h, n; + PC *x; + Func *f, **ff; + + if(!histograms) + return; + + // assign counts to functions. + for(h = 0; h < Ncounters; h++) { + for(x = counters[h]; x != NULL; x = x->next) { + f = findfunc(x->pc); + if(f) { + f->onstack += x->count; + f->leaf += x->count; + } + f = findfunc(x->callerpc); + if(f) + f->leaf -= x->count; + } + } + + // build array + ff = malloc(nfunc*sizeof ff[0]); + if(ff == nil) + sysfatal("out of memory"); + n = 0; + for(h = 0; h < nelem(func); h++) + for(f = func[h]; f != NULL; f = f->next) + ff[n++] = f; + + // sort by leaf counts + qsort(ff, nfunc, sizeof ff[0], compareleaf); + + // print. + fprint(2, "%d samples (avg %.1g threads)\n", nsample, (double)nsamplethread/nsample); + for(i = 0; i < nfunc; i++) { + f = ff[i]; + fprint(2, "%6.2f%%\t", 100.0*(double)f->leaf/nsample); + if(stacks) + fprint(2, "%6.2f%%\t", 100.0*(double)f->onstack/nsample); + fprint(2, "%s\n", f->s.name); + } +} + +typedef struct Trace Trace; +struct Trace { + int count; + int npc; + uvlong *pc; + Trace *next; +}; + +void +dumppprof(void) +{ + uvlong i, n, *p, *e; + int ntrace; + Trace *trace, *tp, *up, *prev; + + if(!pprof) + return; + e = ppdata + nppdata; + // Create list of traces. First, count the traces + ntrace = 0; + for(p = ppdata; p < e;) { + n = *p++; + p += n; + if(n == 0) + continue; + ntrace++; + } + if(ntrace <= 0) + return; + // Allocate and link the traces together. + trace = malloc(ntrace * sizeof(Trace)); + if(trace == nil) + sysfatal("out of memory"); + tp = trace; + for(p = ppdata; p < e;) { + n = *p++; + if(n == 0) + continue; + tp->count = 1; + tp->npc = n; + tp->pc = p; + tp->next = tp+1; + tp++; + p += n; + } + trace[ntrace-1].next = nil; + // Eliminate duplicates. Lousy algorithm, although not as bad as it looks because + // the list collapses fast. + for(tp = trace; tp != nil; tp = tp->next) { + prev = tp; + for(up = tp->next; up != nil; up = up->next) { + if(up->npc == tp->npc && memcmp(up->pc, tp->pc, up->npc*sizeof up->pc[0]) == 0) { + tp->count++; + prev->next = up->next; + } else { + prev = up; + } + } + } + // Write file. + // See http://code.google.com/p/google-perftools/source/browse/trunk/doc/cpuprofile-fileformat.html + // 1) Header + arch->ppword(0); // must be zero + arch->ppword(3); // 3 words follow in header + arch->ppword(0); // must be zero + arch->ppword(delta_msec * 1000); // sampling period in microseconds + arch->ppword(0); // must be zero (padding) + // 2) One record for each trace. + for(tp = trace; tp != nil; tp = tp->next) { + arch->ppword(tp->count); + arch->ppword(tp->npc); + for(i = 0; i < tp->npc; i++) { + arch->ppword(tp->pc[i]); + } + } + // 3) Binary trailer + arch->ppword(0); // must be zero + arch->ppword(1); // must be one + arch->ppword(0); // must be zero + // 4) Mapped objects. + Bwrite(pproffd, ppmapdata, strlen(ppmapdata)); + // 5) That's it. + Bterm(pproffd); +} + +int +startprocess(char **argv) +{ + int pid; + + if((pid = fork()) == 0) { + pid = getpid(); + if(ctlproc(pid, "hang") < 0){ + fprint(2, "prof: child process could not hang\n"); + exits(0); + } + execv(argv[0], argv); + fprint(2, "prof: could not exec %s: %r\n", argv[0]); + exits(0); + } + + if(pid == -1) { + fprint(2, "prof: could not fork\n"); + exit(1); + } + if(ctlproc(pid, "attached") < 0 || ctlproc(pid, "waitstop") < 0) { + fprint(2, "prof: could not attach to child process: %r\n"); + exit(1); + } + return pid; +} + +void +detach(void) +{ + int i; + + for(i = 0; i < nthread; i++) + detachproc(map[i]); +} + +int +main(int argc, char *argv[]) +{ + int i; + char *ppfile; + + ARGBEGIN{ + case 'P': + pprof =1; + ppfile = EARGF(Usage()); + pproffd = Bopen(ppfile, OWRITE); + if(pproffd == nil) { + fprint(2, "prof: cannot open %s: %r\n", ppfile); + exit(2); + } + break; + case 'd': + delta_msec = atoi(EARGF(Usage())); + break; + case 't': + total_sec = atoi(EARGF(Usage())); + break; + case 'p': + pid = atoi(EARGF(Usage())); + break; + case 'f': + functions = 1; + break; + case 'h': + histograms = 1; + break; + case 'l': + linenums = 1; + break; + case 'r': + registers = 1; + break; + case 's': + stacks++; + break; + default: + Usage(); + }ARGEND + if(pid <= 0 && argc == 0) + Usage(); + if(functions+linenums+registers+stacks+pprof == 0) + histograms = 1; + if(!machbyname("amd64")) { + fprint(2, "prof: no amd64 support\n", pid); + exit(1); + } + if(argc > 0) + file = argv[0]; + else if(pid) { + file = proctextfile(pid); + if (file == NULL) { + fprint(2, "prof: can't find file for pid %d: %r\n", pid); + fprint(2, "prof: on Darwin, need to provide file name explicitly\n"); + exit(1); + } + } + fd = open(file, 0); + if(fd < 0) { + fprint(2, "prof: can't open %s: %r\n", file); + exit(1); + } + if(crackhdr(fd, &fhdr)) { + have_syms = syminit(fd, &fhdr); + if(!have_syms) { + fprint(2, "prof: no symbols for %s: %r\n", file); + } + } else { + fprint(2, "prof: crack header for %s: %r\n", file); + exit(1); + } + if(pid <= 0) + pid = startprocess(argv); + attachproc(pid, &fhdr); // initializes thread list + if(setarch() < 0) { + detach(); + fprint(2, "prof: can't identify binary architecture for pid %d\n", pid); + exit(1); + } + if(getthreads() <= 0) { + detach(); + fprint(2, "prof: can't find threads for pid %d\n", pid); + exit(1); + } + for(i = 0; i < nthread; i++) + ctlproc(thread[i], "start"); + samples(); + detach(); + dumphistogram(); + dumppprof(); + exit(0); +} diff --git a/src/cmd/vet/Makefile b/src/cmd/vet/Makefile index 2a35d1ae3..307f4729c 100644 --- a/src/cmd/vet/Makefile +++ b/src/cmd/vet/Makefile @@ -3,5 +3,6 @@ # license that can be found in the LICENSE file. test testshort: - go build - ../../../test/errchk ./vet -printfuncs='Warn:1,Warnf:1' print.go + go build -tags vet_test + ../../../test/errchk ./vet -compositewhitelist=false -printfuncs='Warn:1,Warnf:1' *.go + diff --git a/src/cmd/vet/atomic.go b/src/cmd/vet/atomic.go new file mode 100644 index 000000000..4ab256f64 --- /dev/null +++ b/src/cmd/vet/atomic.go @@ -0,0 +1,59 @@ +// 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 main + +import ( + "go/ast" + "go/token" +) + +// checkAtomicAssignment walks the assignment statement checking for common +// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) +func (f *File) checkAtomicAssignment(n *ast.AssignStmt) { + if !vet("atomic") { + return + } + + if len(n.Lhs) != len(n.Rhs) { + return + } + + for i, right := range n.Rhs { + call, ok := right.(*ast.CallExpr) + if !ok { + continue + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + pkg, ok := sel.X.(*ast.Ident) + if !ok || pkg.Name != "atomic" { + continue + } + + switch sel.Sel.Name { + case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": + f.checkAtomicAddAssignment(n.Lhs[i], call) + } + } +} + +// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value +// to the same variable being used in the operation +func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { + arg := call.Args[0] + broken := false + + if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { + broken = f.gofmt(left) == f.gofmt(uarg.X) + } else if star, ok := left.(*ast.StarExpr); ok { + broken = f.gofmt(star.X) == f.gofmt(arg) + } + + if broken { + f.Warn(left.Pos(), "direct assignment to atomic value") + } +} diff --git a/src/cmd/vet/buildtag.go b/src/cmd/vet/buildtag.go new file mode 100644 index 000000000..4b7580457 --- /dev/null +++ b/src/cmd/vet/buildtag.go @@ -0,0 +1,91 @@ +// 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 main + +import ( + "bytes" + "fmt" + "os" + "strings" + "unicode" +) + +var ( + nl = []byte("\n") + slashSlash = []byte("//") + plusBuild = []byte("+build") +) + +// checkBuildTag checks that build tags are in the correct location and well-formed. +func checkBuildTag(name string, data []byte) { + if !vet("buildtags") { + return + } + lines := bytes.SplitAfter(data, nl) + + // Determine cutpoint where +build comments are no longer valid. + // They are valid in leading // comments in the file followed by + // a blank line. + var cutoff int + for i, line := range lines { + line = bytes.TrimSpace(line) + if len(line) == 0 { + cutoff = i + continue + } + if bytes.HasPrefix(line, slashSlash) { + continue + } + break + } + + for i, line := range lines { + line = bytes.TrimSpace(line) + if !bytes.HasPrefix(line, slashSlash) { + continue + } + text := bytes.TrimSpace(line[2:]) + if bytes.HasPrefix(text, plusBuild) { + fields := bytes.Fields(text) + if !bytes.Equal(fields[0], plusBuild) { + // Comment is something like +buildasdf not +build. + fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) + continue + } + if i >= cutoff { + fmt.Fprintf(os.Stderr, "%s:%d: +build comment appears too late in file\n", name, i+1) + setExit(1) + continue + } + // Check arguments. + Args: + for _, arg := range fields[1:] { + for _, elem := range strings.Split(string(arg), ",") { + if strings.HasPrefix(elem, "!!") { + fmt.Fprintf(os.Stderr, "%s:%d: invalid double negative in build constraint: %s\n", name, i+1, arg) + setExit(1) + break Args + } + if strings.HasPrefix(elem, "!") { + elem = elem[1:] + } + for _, c := range elem { + if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' { + fmt.Fprintf(os.Stderr, "%s:%d: invalid non-alphanumeric build constraint: %s\n", name, i+1, arg) + setExit(1) + break Args + } + } + } + } + continue + } + // Comment with +build but not at beginning. + if bytes.Contains(line, plusBuild) && i < cutoff { + fmt.Fprintf(os.Stderr, "%s:%d: possible malformed +build comment\n", name, i+1) + continue + } + } +} diff --git a/src/cmd/vet/buildtag_bad.go b/src/cmd/vet/buildtag_bad.go new file mode 100644 index 000000000..4dca6a443 --- /dev/null +++ b/src/cmd/vet/buildtag_bad.go @@ -0,0 +1,11 @@ +// This file contains misplaced or malformed build constraints. +// The Go tool will skip it, because the constraints are invalid. +// It serves only to test the tag checker during make test. + +// Mention +build // ERROR "possible malformed \+build comment" + +// +build !!bang // ERROR "invalid double negative in build constraint" +// +build @#$ // ERROR "invalid non-alphanumeric build constraint" + +// +build toolate // ERROR "build comment appears too late in file" +package main diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go index 620964aaf..f164eaca2 100644 --- a/src/cmd/vet/doc.go +++ b/src/cmd/vet/doc.go @@ -9,9 +9,12 @@ calls whose arguments do not align with the format string. Vet uses heuristics that do not guarantee all reports are genuine problems, but it can find errors not caught by the compilers. +By default all checks are performed, but if explicit flags are provided, only +those identified by the flags are performed. + Available checks: -1. Printf family +1. Printf family, flag -printf Suspicious calls to functions in the Printf family, including any functions with these names: @@ -28,24 +31,29 @@ complains about arguments that look like format descriptor strings. It also checks for errors such as using a Writer as the first argument of Printf. -2. Methods +2. Methods, flag -methods Non-standard signatures for methods with familiar names, including: Format GobEncode GobDecode MarshalJSON MarshalXML - Peek ReadByte ReadFrom ReadRune Scan Seek + Peek ReadByte ReadFrom ReadRune Scan Seek UnmarshalJSON UnreadByte UnreadRune WriteByte WriteTo -3. Struct tags +3. Struct tags, flag -structtags Struct tags that do not follow the format understood by reflect.StructTag.Get. +4. Untagged composite literals, flag -composites + +Composite struct literals that do not use the type-tagged syntax. + + Usage: go tool vet [flag] [file.go ...] go tool vet [flag] [directory ...] # Scan all .go files under directory, recursively -The flags are: +The other flags are: -v Verbose mode -printfuncs @@ -59,4 +67,4 @@ The flags are: -printfuncs=Warn:1,Warnf:1 */ -package documentation +package main diff --git a/src/cmd/vet/main.go b/src/cmd/vet/main.go index 625133315..20f6cca1a 100644 --- a/src/cmd/vet/main.go +++ b/src/cmd/vet/main.go @@ -11,9 +11,12 @@ import ( "flag" "fmt" "go/ast" + "go/build" "go/parser" + "go/printer" "go/token" - "io" + "go/types" + "io/ioutil" "os" "path/filepath" "strconv" @@ -23,6 +26,24 @@ import ( var verbose = flag.Bool("v", false, "verbose") var exitCode = 0 +// Flags to control which checks to perform. "all" is set to true here, and disabled later if +// a flag is set explicitly. +var report = map[string]*bool{ + "all": flag.Bool("all", true, "check everything; disabled if any explicit check is requested"), + "atomic": flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package"), + "buildtags": flag.Bool("buildtags", false, "check that +build tags are valid"), + "composites": flag.Bool("composites", false, "check that composite literals used type-tagged elements"), + "methods": flag.Bool("methods", false, "check that canonically named methods are canonically defined"), + "printf": flag.Bool("printf", false, "check printf-like invocations"), + "structtags": flag.Bool("structtags", false, "check that struct field tags have canonical format"), + "rangeloops": flag.Bool("rangeloops", false, "check that range loop variables are used correctly"), +} + +// vet tells whether to report errors for the named check, a flag name. +func vet(name string) bool { + return *report["all"] || *report[name] +} + // setExit sets the value for os.Exit when it is called, later. It // remembers the highest value. func setExit(err int) { @@ -34,6 +55,8 @@ func setExit(err int) { // Usage is a replacement usage function for the flags package. func Usage() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n") + fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n") flag.PrintDefaults() os.Exit(2) } @@ -41,7 +64,9 @@ func Usage() { // File is a wrapper for the state of a file used in the parser. // The parse tree walkers are all methods of this type. type File struct { + pkg *Package fset *token.FileSet + name string file *ast.File b bytes.Buffer // for use by methods } @@ -50,6 +75,14 @@ func main() { flag.Usage = Usage flag.Parse() + // If a check is named explicitly, turn off the 'all' flag. + for name, ptr := range report { + if name != "all" && *ptr { + *report["all"] = false + break + } + } + if *printfuncs != "" { for _, name := range strings.Split(*printfuncs, ",") { if len(name) == 0 { @@ -74,41 +107,135 @@ func main() { } if flag.NArg() == 0 { - doFile("stdin", os.Stdin) - } else { + Usage() + } + dirs := false + files := false + for _, name := range flag.Args() { + // Is it a directory? + fi, err := os.Stat(name) + if err != nil { + warnf("error walking tree: %s", err) + continue + } + if fi.IsDir() { + dirs = true + } else { + files = true + } + } + if dirs && files { + Usage() + } + if dirs { for _, name := range flag.Args() { - // Is it a directory? - if fi, err := os.Stat(name); err == nil && fi.IsDir() { - walkDir(name) - } else { - doFile(name, nil) - } + walkDir(name) } + return } + doPackage(flag.Args()) os.Exit(exitCode) } -// doFile analyzes one file. If the reader is nil, the source code is read from the -// named file. -func doFile(name string, reader io.Reader) { - fs := token.NewFileSet() - parsedFile, err := parser.ParseFile(fs, name, reader, 0) +// prefixDirectory places the directory name on the beginning of each name in the list. +func prefixDirectory(directory string, names []string) { + if directory != "." { + for i, name := range names { + names[i] = filepath.Join(directory, name) + } + } +} + +// doPackageDir analyzes the single package found in the directory, if there is one, +// plus a test package, if there is one. +func doPackageDir(directory string) { + pkg, err := build.Default.ImportDir(directory, 0) if err != nil { - errorf("%s: %s", name, err) + // If it's just that there are no go source files, that's fine. + if _, nogo := err.(*build.NoGoError); nogo { + return + } + // Non-fatal: we are doing a recursive walk and there may be other directories. + warnf("cannot process directory %s: %s", directory, err) return } - file := &File{fset: fs, file: parsedFile} - file.walkFile(name, parsedFile) + var names []string + names = append(names, pkg.GoFiles...) + names = append(names, pkg.CgoFiles...) + names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package. + prefixDirectory(directory, names) + doPackage(names) + // Is there also a "foo_test" package? If so, do that one as well. + if len(pkg.XTestGoFiles) > 0 { + names = pkg.XTestGoFiles + prefixDirectory(directory, names) + doPackage(names) + } +} + +type Package struct { + types map[ast.Expr]types.Type + values map[ast.Expr]interface{} +} + +// doPackage analyzes the single package constructed from the named files. +func doPackage(names []string) { + var files []*File + var astFiles []*ast.File + fs := token.NewFileSet() + for _, name := range names { + f, err := os.Open(name) + if err != nil { + errorf("%s: %s", name, err) + } + defer f.Close() + data, err := ioutil.ReadAll(f) + if err != nil { + errorf("%s: %s", name, err) + } + checkBuildTag(name, data) + parsedFile, err := parser.ParseFile(fs, name, bytes.NewReader(data), 0) + if err != nil { + errorf("%s: %s", name, err) + } + files = append(files, &File{fset: fs, name: name, file: parsedFile}) + astFiles = append(astFiles, parsedFile) + } + pkg := new(Package) + pkg.types = make(map[ast.Expr]types.Type) + pkg.values = make(map[ast.Expr]interface{}) + exprFn := func(x ast.Expr, typ types.Type, val interface{}) { + pkg.types[x] = typ + if val != nil { + pkg.values[x] = val + } + } + // By providing the Context with our own error function, it will continue + // past the first error. There is no need for that function to do anything. + context := types.Context{ + Expr: exprFn, + Error: func(error) {}, + } + // Type check the package. + _, err := context.Check(fs, astFiles) + if err != nil && *verbose { + warnf("%s", err) + } + for _, file := range files { + file.pkg = pkg + file.walkFile(file.name, file.file) + } } func visit(path string, f os.FileInfo, err error) error { if err != nil { errorf("walk error: %s", err) - return nil } - if !f.IsDir() && strings.HasSuffix(path, ".go") { - doFile(path, nil) + // One package per directory. Ignore the files themselves. + if !f.IsDir() { + return nil } + doPackageDir(path) return nil } @@ -117,11 +244,18 @@ func walkDir(root string) { filepath.Walk(root, visit) } -// error formats the error to standard error, adding program -// identification and a newline +// errorf formats the error to standard error, adding program +// identification and a newline, and exits. func errorf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) - setExit(2) + os.Exit(2) +} + +// warnf formats the error to standard error, adding program +// identification and a newline, but does not exit. +func warnf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...) + setExit(1) } // Println is fmt.Println guarded by -v. @@ -152,16 +286,22 @@ func (f *File) Badf(pos token.Pos, format string, args ...interface{}) { setExit(1) } +func (f *File) loc(pos token.Pos) string { + // Do not print columns. Because the pos often points to the start of an + // expression instead of the inner part with the actual error, the + // precision can mislead. + posn := f.fset.Position(pos) + return fmt.Sprintf("%s:%d: ", posn.Filename, posn.Line) +} + // Warn reports an error but does not set the exit code. func (f *File) Warn(pos token.Pos, args ...interface{}) { - loc := f.fset.Position(pos).String() + ": " - fmt.Fprint(os.Stderr, loc+fmt.Sprintln(args...)) + fmt.Fprint(os.Stderr, f.loc(pos)+fmt.Sprintln(args...)) } // Warnf reports a formatted error but does not set the exit code. func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) { - loc := f.fset.Position(pos).String() + ": " - fmt.Fprintf(os.Stderr, loc+format+"\n", args...) + fmt.Fprintf(os.Stderr, f.loc(pos)+format+"\n", args...) } // walkFile walks the file's tree. @@ -173,6 +313,8 @@ func (f *File) walkFile(name string, file *ast.File) { // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { + case *ast.AssignStmt: + f.walkAssignStmt(n) case *ast.CallExpr: f.walkCallExpr(n) case *ast.CompositeLit: @@ -183,15 +325,32 @@ func (f *File) Visit(node ast.Node) ast.Visitor { f.walkMethodDecl(n) case *ast.InterfaceType: f.walkInterfaceType(n) + case *ast.RangeStmt: + f.walkRangeStmt(n) } return f } +// walkAssignStmt walks an assignment statement +func (f *File) walkAssignStmt(stmt *ast.AssignStmt) { + f.checkAtomicAssignment(stmt) +} + // walkCall walks a call expression. func (f *File) walkCall(call *ast.CallExpr, name string) { f.checkFmtPrintfCall(call, name) } +// walkCallExpr walks a call expression. +func (f *File) walkCallExpr(call *ast.CallExpr) { + switch x := call.Fun.(type) { + case *ast.Ident: + f.walkCall(call, x.Name) + case *ast.SelectorExpr: + f.walkCall(call, x.Sel.Name) + } +} + // walkCompositeLit walks a composite literal. func (f *File) walkCompositeLit(c *ast.CompositeLit) { f.checkUntaggedLiteral(c) @@ -228,12 +387,14 @@ func (f *File) walkInterfaceType(t *ast.InterfaceType) { } } -// walkCallExpr walks a call expression. -func (f *File) walkCallExpr(call *ast.CallExpr) { - switch x := call.Fun.(type) { - case *ast.Ident: - f.walkCall(call, x.Name) - case *ast.SelectorExpr: - f.walkCall(call, x.Sel.Name) - } +// walkRangeStmt walks a range statement. +func (f *File) walkRangeStmt(n *ast.RangeStmt) { + checkRangeLoop(f, n) +} + +// gofmt returns a string representation of the expression. +func (f *File) gofmt(x ast.Expr) string { + f.b.Reset() + printer.Fprint(&f.b, f.fset, x) + return f.b.String() } diff --git a/src/cmd/vet/method.go b/src/cmd/vet/method.go index 41cb40ff9..bf982dc7a 100644 --- a/src/cmd/vet/method.go +++ b/src/cmd/vet/method.go @@ -55,6 +55,9 @@ var canonicalMethods = map[string]MethodSig{ } func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { + if !vet("methods") { + return + } // Expected input/output. expect, ok := canonicalMethods[id.Name] if !ok { @@ -87,9 +90,7 @@ func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { fmt.Fprintf(&f.b, "<%s>", err) } actual := f.b.String() - if strings.HasPrefix(actual, "func(") { - actual = actual[4:] - } + actual = strings.TrimPrefix(actual, "func") actual = id.Name + actual f.Warnf(id.Pos(), "method %s should have signature %s", actual, expectFmt) diff --git a/src/cmd/vet/print.go b/src/cmd/vet/print.go index ee9a33c70..fb0fb9f9b 100644 --- a/src/cmd/vet/print.go +++ b/src/cmd/vet/print.go @@ -8,9 +8,10 @@ package main import ( "flag" - "fmt" "go/ast" "go/token" + "go/types" + "strconv" "strings" "unicode/utf8" ) @@ -43,6 +44,9 @@ var printList = map[string]int{ // checkCall triggers the print-specific checks if the call invokes a print function. func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { + if !vet("printf") { + return + } name := strings.ToLower(Name) if skip, ok := printfList[name]; ok { f.checkPrintf(call, Name, skip) @@ -54,73 +58,134 @@ func (f *File) checkFmtPrintfCall(call *ast.CallExpr, Name string) { } } +// literal returns the literal value represented by the expression, or nil if it is not a literal. +func (f *File) literal(value ast.Expr) *ast.BasicLit { + switch v := value.(type) { + case *ast.BasicLit: + return v + case *ast.ParenExpr: + return f.literal(v.X) + case *ast.BinaryExpr: + if v.Op != token.ADD { + break + } + litX := f.literal(v.X) + litY := f.literal(v.Y) + if litX != nil && litY != nil { + lit := *litX + x, errX := strconv.Unquote(litX.Value) + y, errY := strconv.Unquote(litY.Value) + if errX == nil && errY == nil { + return &ast.BasicLit{ + ValuePos: lit.ValuePos, + Kind: lit.Kind, + Value: strconv.Quote(x + y), + } + } + } + case *ast.Ident: + // See if it's a constant or initial value (we can't tell the difference). + if v.Obj == nil || v.Obj.Decl == nil { + return nil + } + valueSpec, ok := v.Obj.Decl.(*ast.ValueSpec) + if ok && len(valueSpec.Names) == len(valueSpec.Values) { + // Find the index in the list of names + var i int + for i = 0; i < len(valueSpec.Names); i++ { + if valueSpec.Names[i].Name == v.Name { + if lit, ok := valueSpec.Values[i].(*ast.BasicLit); ok { + return lit + } + return nil + } + } + } + } + return nil +} + // checkPrintf checks a call to a formatted print routine such as Printf. -// The skip argument records how many arguments to ignore; that is, -// call.Args[skip] is (well, should be) the format argument. -func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) { - if len(call.Args) <= skip { +// call.Args[formatIndex] is (well, should be) the format argument. +func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { + if formatIndex >= len(call.Args) { return } - // Common case: literal is first argument. - arg := call.Args[skip] - lit, ok := arg.(*ast.BasicLit) - if !ok { - // Too hard to check. + lit := f.literal(call.Args[formatIndex]) + if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-literal format in call to", name) } return } - if lit.Kind == token.STRING { - if !strings.Contains(lit.Value, "%") { - if len(call.Args) > skip+1 { - f.Badf(call.Pos(), "no formatting directive in %s call", name) - } - return + if lit.Kind != token.STRING { + f.Badf(call.Pos(), "literal %v not a string in call to", lit.Value, name) + } + format, err := strconv.Unquote(lit.Value) + if err != nil { + // Shouldn't happen if parser returned no errors, but be safe. + f.Badf(call.Pos(), "invalid quoted string literal") + } + firstArg := formatIndex + 1 // Arguments are immediately after format string. + if !strings.Contains(format, "%") { + if len(call.Args) > firstArg { + f.Badf(call.Pos(), "no formatting directive in %s call", name) } + return } // Hard part: check formats against args. - // Trivial but useful test: count. - numArgs := 0 - for i, w := 0, 0; i < len(lit.Value); i += w { + argNum := firstArg + for i, w := 0, 0; i < len(format); i += w { w = 1 - if lit.Value[i] == '%' { - nbytes, nargs := f.parsePrintfVerb(call, lit.Value[i:]) + if format[i] == '%' { + verb, flags, nbytes, nargs := f.parsePrintfVerb(call, format[i:]) w = nbytes - numArgs += nargs + if verb == '%' { // "%%" does nothing interesting. + continue + } + // If we've run out of args, print after loop will pick that up. + if argNum+nargs <= len(call.Args) { + f.checkPrintfArg(call, verb, flags, argNum, nargs) + } + argNum += nargs } } - expect := len(call.Args) - (skip + 1) - if numArgs != expect { - f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect) + // TODO: Dotdotdot is hard. + if call.Ellipsis.IsValid() && argNum != len(call.Args) { + return + } + if argNum != len(call.Args) { + expect := argNum - firstArg + numArgs := len(call.Args) - firstArg + f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs) } } -// parsePrintfVerb returns the number of bytes and number of arguments -// consumed by the Printf directive that begins s, including its percent sign -// and verb. -func (f *File) parsePrintfVerb(call *ast.CallExpr, s string) (nbytes, nargs int) { +// parsePrintfVerb returns the verb that begins the format string, along with its flags, +// the number of bytes to advance the format to step past the verb, and number of +// arguments it consumes. +func (f *File) parsePrintfVerb(call *ast.CallExpr, format string) (verb rune, flags []byte, nbytes, nargs int) { // There's guaranteed a percent sign. - flags := make([]byte, 0, 5) + flags = make([]byte, 0, 5) nbytes = 1 - end := len(s) + end := len(format) // There may be flags. FlagLoop: for nbytes < end { - switch s[nbytes] { + switch format[nbytes] { case '#', '0', '+', '-', ' ': - flags = append(flags, s[nbytes]) + flags = append(flags, format[nbytes]) nbytes++ default: break FlagLoop } } getNum := func() { - if nbytes < end && s[nbytes] == '*' { + if nbytes < end && format[nbytes] == '*' { nbytes++ nargs++ } else { - for nbytes < end && '0' <= s[nbytes] && s[nbytes] <= '9' { + for nbytes < end && '0' <= format[nbytes] && format[nbytes] <= '9' { nbytes++ } } @@ -128,24 +193,38 @@ FlagLoop: // There may be a width. getNum() // If there's a period, there may be a precision. - if nbytes < end && s[nbytes] == '.' { + if nbytes < end && format[nbytes] == '.' { flags = append(flags, '.') // Treat precision as a flag. nbytes++ getNum() } // Now a verb. - c, w := utf8.DecodeRuneInString(s[nbytes:]) + c, w := utf8.DecodeRuneInString(format[nbytes:]) nbytes += w + verb = c if c != '%' { nargs++ - f.checkPrintfVerb(call, c, flags) } return } +// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. +type printfArgType int + +const ( + argBool printfArgType = 1 << iota + argInt + argRune + argString + argFloat + argPointer + anyType printfArgType = ^0 +) + type printVerb struct { verb rune flags string // known flags are all ASCII + typ printfArgType } // Common flag sets for printf verbs. @@ -164,36 +243,57 @@ var printVerbs = []printVerb{ // '+' is required sign for numbers, Go format for %v. // '#' is alternate format for several verbs. // ' ' is spacer for numbers - {'b', numFlag}, - {'c', "-"}, - {'d', numFlag}, - {'e', numFlag}, - {'E', numFlag}, - {'f', numFlag}, - {'F', numFlag}, - {'g', numFlag}, - {'G', numFlag}, - {'o', sharpNumFlag}, - {'p', "-#"}, - {'q', "-+#."}, - {'s', "-."}, - {'t', "-"}, - {'T', "-"}, - {'U', "-#"}, - {'v', allFlags}, - {'x', sharpNumFlag}, - {'X', sharpNumFlag}, + {'b', numFlag, argInt | argFloat}, + {'c', "-", argRune | argInt}, + {'d', numFlag, argInt}, + {'e', numFlag, argFloat}, + {'E', numFlag, argFloat}, + {'f', numFlag, argFloat}, + {'F', numFlag, argFloat}, + {'g', numFlag, argFloat}, + {'G', numFlag, argFloat}, + {'o', sharpNumFlag, argInt}, + {'p', "-#", argPointer}, + {'q', " -+.0#", argRune | argInt | argString}, + {'s', " -+.0", argString}, + {'t', "-", argBool}, + {'T', "-", anyType}, + {'U', "-#", argRune | argInt}, + {'v', allFlags, anyType}, + {'x', sharpNumFlag, argRune | argInt | argString}, + {'X', sharpNumFlag, argRune | argInt | argString}, } const printfVerbs = "bcdeEfFgGopqstTvxUX" -func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { +func (f *File) checkPrintfArg(call *ast.CallExpr, verb rune, flags []byte, argNum, nargs int) { // Linear scan is fast enough for a small list. for _, v := range printVerbs { if v.verb == verb { for _, flag := range flags { if !strings.ContainsRune(v.flags, rune(flag)) { f.Badf(call.Pos(), "unrecognized printf flag for verb %q: %q", verb, flag) + return + } + } + // Verb is good. If nargs>1, we have something like %.*s and all but the final + // arg must be integer. + for i := 0; i < nargs-1; i++ { + if !f.matchArgType(argInt, call.Args[argNum+i]) { + f.Badf(call.Pos(), "arg %s for * in printf format not of type int", f.gofmt(call.Args[argNum+i])) + } + } + for _, v := range printVerbs { + if v.verb == verb { + arg := call.Args[argNum+nargs-1] + if !f.matchArgType(v.typ, arg) { + typeString := "" + if typ := f.pkg.types[arg]; typ != nil { + typeString = typ.String() + } + f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), verb, typeString) + } + break } } return @@ -202,15 +302,67 @@ func (f *File) checkPrintfVerb(call *ast.CallExpr, verb rune, flags []byte) { f.Badf(call.Pos(), "unrecognized printf verb %q", verb) } +func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { + // TODO: for now, we can only test builtin types and untyped constants. + typ := f.pkg.types[arg] + if typ == nil { + return true + } + basic, ok := typ.(*types.Basic) + if !ok { + return true + } + switch basic.Kind { + case types.Bool: + return t&argBool != 0 + case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: + fallthrough + case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: + return t&argInt != 0 + case types.Float32, types.Float64, types.Complex64, types.Complex128: + return t&argFloat != 0 + case types.String: + return t&argString != 0 + case types.UnsafePointer: + return t&(argPointer|argInt) != 0 + case types.UntypedBool: + return t&argBool != 0 + case types.UntypedComplex: + return t&argFloat != 0 + case types.UntypedFloat: + // If it's integral, we can use an int format. + switch f.pkg.values[arg].(type) { + case int, int8, int16, int32, int64: + return t&(argInt|argFloat) != 0 + case uint, uint8, uint16, uint32, uint64: + return t&(argInt|argFloat) != 0 + } + return t&argFloat != 0 + case types.UntypedInt: + return t&argInt != 0 + case types.UntypedRune: + return t&(argInt|argRune) != 0 + case types.UntypedString: + return t&argString != 0 + case types.UntypedNil: + return t&argPointer != 0 // TODO? + case types.Invalid: + if *verbose { + f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg) + } + return true // Probably a type check problem. + } + return false +} + // checkPrint checks a call to an unformatted print routine such as Println. -// The skip argument records how many arguments to ignore; that is, -// call.Args[skip] is the first argument to be printed. -func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { +// call.Args[firstArg] is the first argument to be printed. +func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) { isLn := strings.HasSuffix(name, "ln") isF := strings.HasPrefix(name, "F") args := call.Args // check for Println(os.Stderr, ...) - if skip == 0 && !isF && len(args) > 0 { + if firstArg == 0 && !isF && len(args) > 0 { if sel, ok := args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { @@ -219,13 +371,23 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { } } } - if len(args) <= skip { - if *verbose && !isLn { - f.Badf(call.Pos(), "no args in %s call", name) + if len(args) <= firstArg { + // If we have a call to a method called Error that satisfies the Error interface, + // then it's ok. Otherwise it's something like (*T).Error from the testing package + // and we need to check it. + if name == "Error" && f.isErrorMethodCall(call) { + return + } + // If it's an Error call now, it's probably for printing errors. + if !isLn { + // Check the signature to be sure: there are niladic functions called "error". + if firstArg != 0 || f.numArgsInSignature(call) != firstArg { + f.Badf(call.Pos(), "no args in %s call", name) + } } return } - arg := args[skip] + arg := args[firstArg] if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if strings.Contains(lit.Value, "%") { f.Badf(call.Pos(), "possible formatting directive in %s call", name) @@ -242,37 +404,63 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { } } -// This function never executes, but it serves as a simple test for the program. -// Test with make test. -func BadFunctionUsedInTests() { - fmt.Println() // not an error - fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" - fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args in Printf call" - fmt.Printf("%s%%%d", "hi", 3) // correct - fmt.Printf("%.*d", 3, 3) // correct - fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args in Printf call" - printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("now is the time", "buddy") // ERROR "no formatting directive" - Printf("hi") // ok - f := new(File) - f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" - f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args in Warnf call" - f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" - f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" -} - -type BadTypeUsedInTests struct { - X int "hello" // ERROR "struct field tag" -} - -func (t *BadTypeUsedInTests) Scan(x fmt.ScanState, c byte) { // ERROR "method Scan[(]x fmt.ScanState, c byte[)] should have signature Scan[(]fmt.ScanState, rune[)] error" -} - -type BadInterfaceUsedInTests interface { - ReadByte() byte // ERROR "method ReadByte[(][)] byte should have signature ReadByte[(][)] [(]byte, error[)]" +// numArgsInSignature tells how many formal arguments the function type +// being called has. +func (f *File) numArgsInSignature(call *ast.CallExpr) int { + // Check the type of the function or method declaration + typ := f.pkg.types[call.Fun] + if typ == nil { + return 0 + } + // The type must be a signature, but be sure for safety. + sig, ok := typ.(*types.Signature) + if !ok { + return 0 + } + return len(sig.Params) } -// printf is used by the test. -func printf(format string, args ...interface{}) { - panic("don't call - testing only") +// isErrorMethodCall reports whether the call is of a method with signature +// func Error() string +// where "string" is the universe's string type. We know the method is called "Error". +func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { + // Is it a selector expression? Otherwise it's a function call, not a method call. + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return false + } + // The package is type-checked, so if there are no arguments, we're done. + if len(call.Args) > 0 { + return false + } + // Check the type of the method declaration + typ := f.pkg.types[sel] + if typ == nil { + return false + } + // The type must be a signature, but be sure for safety. + sig, ok := typ.(*types.Signature) + if !ok { + return false + } + // There must be a receiver for it to be a method call. Otherwise it is + // a function, not something that satisfies the error interface. + if sig.Recv == nil { + return false + } + // There must be no arguments. Already verified by type checking, but be thorough. + if len(sig.Params) > 0 { + return false + } + // Finally the real questions. + // There must be one result. + if len(sig.Results) != 1 { + return false + } + // It must have return type "string" from the universe. + result := sig.Results[0].Type + if types.IsIdentical(result, types.Typ[types.String]) { + return true + } + return false } diff --git a/src/cmd/vet/rangeloop.go b/src/cmd/vet/rangeloop.go new file mode 100644 index 000000000..ecc595427 --- /dev/null +++ b/src/cmd/vet/rangeloop.go @@ -0,0 +1,65 @@ +// 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 contains the code to check range loop variables bound inside function +literals that are deferred or launched in new goroutines. We only check +instances where the defer or go statement is the last statement in the loop +body, as otherwise we would need whole program analysis. + +For example: + + for i, v := range s { + go func() { + println(i, v) // not what you might expect + }() + } + +See: http://golang.org/doc/go_faq.html#closures_and_goroutines +*/ + +package main + +import "go/ast" + +// checkRangeLoop walks the body of the provided range statement, checking if +// its index or value variables are used unsafely inside goroutines or deferred +// function literals. +func checkRangeLoop(f *File, n *ast.RangeStmt) { + if !vet("rangeloops") { + return + } + key, _ := n.Key.(*ast.Ident) + val, _ := n.Value.(*ast.Ident) + if key == nil && val == nil { + return + } + sl := n.Body.List + if len(sl) == 0 { + return + } + var last *ast.CallExpr + switch s := sl[len(sl)-1].(type) { + case *ast.GoStmt: + last = s.Call + case *ast.DeferStmt: + last = s.Call + default: + return + } + lit, ok := last.Fun.(*ast.FuncLit) + if !ok { + return + } + ast.Inspect(lit.Body, func(n ast.Node) bool { + id, ok := n.(*ast.Ident) + if !ok || id.Obj == nil { + return true + } + if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { + f.Warn(id.Pos(), "range variable", id.Name, "enclosed by function") + } + return true + }) +} diff --git a/src/cmd/vet/structtag.go b/src/cmd/vet/structtag.go index ea2a9d863..545e420c1 100644 --- a/src/cmd/vet/structtag.go +++ b/src/cmd/vet/structtag.go @@ -14,6 +14,9 @@ import ( // checkField checks a struct field tag. func (f *File) checkCanonicalFieldTag(field *ast.Field) { + if !vet("structtags") { + return + } if field.Tag == nil { return } diff --git a/src/cmd/vet/taglit.go b/src/cmd/vet/taglit.go index c3c4f3234..0324e37b0 100644 --- a/src/cmd/vet/taglit.go +++ b/src/cmd/vet/taglit.go @@ -7,13 +7,38 @@ package main import ( + "flag" "go/ast" + "go/types" "strings" ) +var compositeWhiteList = flag.Bool("compositewhitelist", true, "use composite white list; for testing only") + // checkUntaggedLiteral checks if a composite literal is an struct literal with // untagged fields. func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { + if !vet("composites") { + return + } + + // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible. + typ := f.pkg.types[c] + if typ != nil { + // If it's a named type, pull out the underlying type. + if namedType, ok := typ.(*types.NamedType); ok { + typ = namedType.Underlying + } + switch typ.(type) { + case *types.Slice: + return + case *types.Array: + return + } + } + + // It's a struct, or we can't tell it's not a struct because we don't have types. + // Check if the CompositeLit contains an untagged field. allKeyValue := true for _, e := range c.Elts { @@ -42,12 +67,12 @@ func (f *File) checkUntaggedLiteral(c *ast.CompositeLit) { f.Warnf(c.Pos(), "unresolvable package for %s.%s literal", pkg.Name, s.Sel.Name) return } - typ := path + "." + s.Sel.Name - if untaggedLiteralWhitelist[typ] { + typeName := path + "." + s.Sel.Name + if *compositeWhiteList && untaggedLiteralWhitelist[typeName] { return } - f.Warnf(c.Pos(), "%s struct literal uses untagged fields", typ) + f.Warnf(c.Pos(), "%s composite literal uses untagged fields", typ) } // pkgPath returns the import path "image/png" for the package name "png". diff --git a/src/cmd/vet/test_atomic.go b/src/cmd/vet/test_atomic.go new file mode 100644 index 000000000..9231e9dc0 --- /dev/null +++ b/src/cmd/vet/test_atomic.go @@ -0,0 +1,43 @@ +// 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. + +// +build vet_test + +// This file contains tests for the atomic checker. + +package main + +import ( + "sync/atomic" +) + +type Counter uint64 + +func AtomicTests() { + x := uint64(1) + x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" + _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value" + x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value" + + y := &x + *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value" + + var su struct{ Counter uint64 } + su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value" + z1 := atomic.AddUint64(&su.Counter, 1) + _ = z1 // Avoid err "z declared and not used" + + var sp struct{ Counter *uint64 } + *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value" + z2 := atomic.AddUint64(sp.Counter, 1) + _ = z2 // Avoid err "z declared and not used" + + au := []uint64{10, 20} + au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value" + au[1] = atomic.AddUint64(&au[0], 1) + + ap := []*uint64{&au[0], &au[1]} + *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value" + *ap[1] = atomic.AddUint64(ap[0], 1) +} diff --git a/src/cmd/vet/test_buildtag.go b/src/cmd/vet/test_buildtag.go new file mode 100644 index 000000000..d7174ade2 --- /dev/null +++ b/src/cmd/vet/test_buildtag.go @@ -0,0 +1,15 @@ +// 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. + +// This file contains tests for the buildtag checker. + +// +build vet_test +// +builder // ERROR "possible malformed \+build comment" +// +build !ignore + +package main + +// +build toolate // ERROR "build comment appears too late in file" + +var _ = 3 diff --git a/src/cmd/vet/test_method.go b/src/cmd/vet/test_method.go new file mode 100644 index 000000000..41de62bb1 --- /dev/null +++ b/src/cmd/vet/test_method.go @@ -0,0 +1,24 @@ +// 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. + +// This file contains tests for the canonical method checker. + +// +build vet_test + +// This file contains the code to check canonical methods. + +package main + +import ( + "fmt" +) + +type MethodTest int + +func (t *MethodTest) Scan(x fmt.ScanState, c byte) { // ERROR "should have signature Scan" +} + +type MethodTestInterface interface { + ReadByte() byte // ERROR "should have signature ReadByte" +} diff --git a/src/cmd/vet/test_print.go b/src/cmd/vet/test_print.go new file mode 100644 index 000000000..8b41e6c69 --- /dev/null +++ b/src/cmd/vet/test_print.go @@ -0,0 +1,153 @@ +// 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. + +// +build vet_test + +// This file contains tests for the printf checker. + +package main + +import ( + "fmt" + "unsafe" // just for test case printing unsafe.Pointer +) + +func UnsafePointerPrintfTest() { + var up unsafe.Pointer + fmt.Printf("%p, %x %X", up, up, up) +} + +// Error methods that do not satisfy the Error interface and should be checked. +type errorTest1 int + +func (errorTest1) Error(...interface{}) string { + return "hi" +} + +type errorTest2 int // Analogous to testing's *T type. +func (errorTest2) Error(...interface{}) { +} + +type errorTest3 int + +func (errorTest3) Error() { // No return value. +} + +type errorTest4 int + +func (errorTest4) Error() int { // Different return type. + return 3 +} + +type errorTest5 int + +func (errorTest5) error() { // niladic; don't complain if no args (was bug) +} + +// This function never executes, but it serves as a simple test for the program. +// Test with make test. +func PrintfTests() { + var b bool + var i int + var r rune + var s string + var x float64 + var p *int + // Some good format/argtypes + fmt.Printf("") + fmt.Printf("%b %b", 3, i) + fmt.Printf("%c %c %c %c", 3, i, 'x', r) + fmt.Printf("%d %d", 3, i) + fmt.Printf("%e %e", 3e9, x) + fmt.Printf("%E %E", 3e9, x) + fmt.Printf("%f %f", 3e9, x) + fmt.Printf("%F %F", 3e9, x) + fmt.Printf("%g %g", 3e9, x) + fmt.Printf("%G %G", 3e9, x) + fmt.Printf("%o %o", 3, i) + fmt.Printf("%p %p", p, nil) + fmt.Printf("%q %q %q %q", 3, i, 'x', r) + fmt.Printf("%s %s", "hi", s) + fmt.Printf("%t %t", true, b) + fmt.Printf("%T %T", 3, i) + fmt.Printf("%U %U", 3, i) + fmt.Printf("%v %v", 3, i) + fmt.Printf("%x %x %x %x", 3, i, "hi", s) + fmt.Printf("%X %X %X %X", 3, i, "hi", s) + fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3) + // Some bad format/argTypes + fmt.Printf("%b", "hi") // ERROR "arg .hi. for printf verb %b of wrong type" + fmt.Printf("%c", 2.3) // ERROR "arg 2.3 for printf verb %c of wrong type" + fmt.Printf("%d", 2.3) // ERROR "arg 2.3 for printf verb %d of wrong type" + fmt.Printf("%e", "hi") // ERROR "arg .hi. for printf verb %e of wrong type" + fmt.Printf("%E", true) // ERROR "arg true for printf verb %E of wrong type" + fmt.Printf("%f", "hi") // ERROR "arg .hi. for printf verb %f of wrong type" + fmt.Printf("%F", 'x') // ERROR "arg 'x' for printf verb %F of wrong type" + fmt.Printf("%g", "hi") // ERROR "arg .hi. for printf verb %g of wrong type" + fmt.Printf("%G", i) // ERROR "arg i for printf verb %G of wrong type" + fmt.Printf("%o", x) // ERROR "arg x for printf verb %o of wrong type" + fmt.Printf("%p", 23) // ERROR "arg 23 for printf verb %p of wrong type" + fmt.Printf("%q", x) // ERROR "arg x for printf verb %q of wrong type" + fmt.Printf("%s", b) // ERROR "arg b for printf verb %s of wrong type" + fmt.Printf("%t", 23) // ERROR "arg 23 for printf verb %t of wrong type" + fmt.Printf("%U", x) // ERROR "arg x for printf verb %U of wrong type" + fmt.Printf("%x", nil) // ERROR "arg nil for printf verb %x of wrong type" + fmt.Printf("%X", 2.3) // ERROR "arg 2.3 for printf verb %X of wrong type" + fmt.Printf("%.*s %d %g", 3, "hi", 23, 'x') // ERROR "arg 'x' for printf verb %g of wrong type" + // TODO + fmt.Println() // not an error + fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" + fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%"+("s"), "hi", 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%s%%%d", "hi", 3) // correct + fmt.Printf("%08s", "woo") // correct + fmt.Printf("% 8s", "woo") // correct + fmt.Printf("%.*d", 3, 3) // correct + fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args for format in Printf call" + fmt.Printf("%.*d", "hi", 3) // ERROR "arg .hi. for \* in printf format not of type int" + fmt.Printf("%.*d", i, 3) // correct + fmt.Printf("%.*d", s, 3) // ERROR "arg s for \* in printf format not of type int" + fmt.Printf("%q %q", multi()...) // ok + fmt.Printf("%#q", `blah`) // ok + printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("hi") // ok + const format = "%s %s\n" + Printf(format, "hi", "there") + Printf(format, "hi") // ERROR "wrong number of args for format in Printf call" + f := new(File) + f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" + f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args for format in Warnf call" + f.Warnf(0, "%r", "hello") // ERROR "unrecognized printf verb" + f.Warnf(0, "%#s", "hello") // ERROR "unrecognized printf flag" + // Something that satisfies the error interface. + var e error + fmt.Println(e.Error()) // ok + // Something that looks like an error interface but isn't, such as the (*T).Error method + // in the testing package. + var et1 errorTest1 + fmt.Println(et1.Error()) // ERROR "no args in Error call" + fmt.Println(et1.Error("hi")) // ok + fmt.Println(et1.Error("%d", 3)) // ERROR "possible formatting directive in Error call" + var et2 errorTest2 + et2.Error() // ERROR "no args in Error call" + et2.Error("hi") // ok, not an error method. + et2.Error("%d", 3) // ERROR "possible formatting directive in Error call" + var et3 errorTest3 + et3.Error() // ok, not an error method. + var et4 errorTest4 + et4.Error() // ok, not an error method. + var et5 errorTest5 + et5.error() // ok, not an error method. +} + +// printf is used by the test. +func printf(format string, args ...interface{}) { + panic("don't call - testing only") +} + +// multi is used by the test. +func multi() []interface{} { + panic("don't call - testing only") +} diff --git a/src/cmd/vet/test_rangeloop.go b/src/cmd/vet/test_rangeloop.go new file mode 100644 index 000000000..941fd72aa --- /dev/null +++ b/src/cmd/vet/test_rangeloop.go @@ -0,0 +1,61 @@ +// 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 contains tests for the rangeloop checker. + +// +build vet_test + +package main + +func RangeLoopTests() { + var s []int + for i, v := range s { + go func() { + println(i) // ERROR "range variable i enclosed by function" + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i, v := range s { + defer func() { + println(i) // ERROR "range variable i enclosed by function" + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i := range s { + go func() { + println(i) // ERROR "range variable i enclosed by function" + }() + } + for _, v := range s { + go func() { + println(v) // ERROR "range variable v enclosed by function" + }() + } + for i, v := range s { + go func() { + println(i, v) + }() + println("unfortunately, we don't catch the error above because of this statement") + } + for i, v := range s { + go func(i, v int) { + println(i, v) + }(i, v) + } + for i, v := range s { + i, v := i, v + go func() { + println(i, v) + }() + } + // If the key of the range statement is not an identifier + // the code should not panic (it used to). + var x [2]int + var f int + for x[0], f = range s { + go func() { + _ = f // ERROR "range variable f enclosed by function" + }() + } +} diff --git a/src/cmd/vet/test_structtag.go b/src/cmd/vet/test_structtag.go new file mode 100644 index 000000000..08cf737fd --- /dev/null +++ b/src/cmd/vet/test_structtag.go @@ -0,0 +1,15 @@ +// 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. + +// This file contains tests for the structtag checker. + +// +build vet_test + +// This file contains the test for canonical struct tags. + +package main + +type StructTagTest struct { + X int "hello" // ERROR "not compatible with reflect.StructTag.Get" +} diff --git a/src/cmd/vet/test_taglit.go b/src/cmd/vet/test_taglit.go new file mode 100644 index 000000000..0d83b18fd --- /dev/null +++ b/src/cmd/vet/test_taglit.go @@ -0,0 +1,31 @@ +// 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 contains tests for the untagged struct literal checker. + +// +build vet_test + +// This file contains the test for untagged struct literals. + +package main + +import ( + "flag" + "go/scanner" +) + +// Testing is awkward because we need to reference things from a separate package +// to trigger the warnings. + +var BadStructLiteralUsedInTests = flag.Flag{ // ERROR "untagged fields" + "Name", + "Usage", + nil, // Value + "DefValue", +} + +// Used to test the check for slices and arrays: If that test is disabled and +// vet is run with --compositewhitelist=false, this line triggers an error. +// Clumsy but sufficient. +var scannerErrorListTest = scanner.ErrorList{nil, nil} diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go index 4a2c2a314..792c104e3 100644 --- a/src/cmd/yacc/doc.go +++ b/src/cmd/yacc/doc.go @@ -48,4 +48,4 @@ referenced by yacc's generated code. Setting it to distinct values allows multiple grammars to be placed in a single package. */ -package documentation +package main diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt index ddb2bc294..df8f567d9 100644 --- a/src/cmd/yacc/units.txt +++ b/src/cmd/yacc/units.txt @@ -215,58 +215,58 @@ lumen cd sr lux cd sr/m² / MONEY DATE -/ Thu Sep 10 2009 - -argentinapeso 1 | 0.2595 $ -australiadollar 1 | 0.8618 $ -boliviaboliviano 1 | 0.1425 $ -brazilreal 1 | 0.5522 $ -britainpound 1 | 1.6651 $ -canadadollar 1 | 0.9277 $ -chilepeso 1 | 0.0018 $ -chinayuan 1 | 0.1464 $ -colombiapeso 1 | 0.0005 $ -czechkoruna 1 | 0.0572 $ -denmarkkrone 1 | 0.1958 $ -dominicanpeso 1 | 0.0278 $ -egyptpound 1 | 0.181 $ -elsalvadorcolon 1 | 0.1143 $ -europeuro 1 | 1.4577 $ -guatemalaquetzal 1 | 0.121 $ -honduraslempira 1 | 0.0529 $ -hongkongdollar 1 | 0.129 $ -hungaryforint 1 | 0.0054 $ -indiarupee 1 | 0.0207 $ -indonesiarupiah 1 | 0.0001 $ -israelshekel 1 | 0.2643 $ -japanyen 1 | 0.0109 $ -kenyashilling 1 | 0.0132 $ -kuwaitdinar 1 | 3.4854 $ -lebanonpound 1 | 0.0007 $ -malaysiaringgit 1 | 0.286 $ -mexicopeso 1 | 0.0748 $ -newzealanddollar 1 | 0.7028 $ -nicaraguacordoba 1 | 0.0487 $ -norwaykrone 1 | 0.1681 $ -pakistanrupee 1 | 0.0121 $ -paraguayguarani 1 | 0.0002 $ -perunewsol 1 | 0.3384 $ -philippinespeso 1 | 0.0207 $ -polandzloty 1 | 0.352 $ -russiaruble 1 | 0.0324 $ -saudiarabiariyal 1 | 0.2666 $ -singaporedollar 1 | 0.7018 $ -slovakkoruna 1 | 0.0484 $ -southafricarand 1 | 0.132 $ -southkoreawon 1 | 0.0008 $ -swedenkrona 1 | 0.1429 $ -switzerlandfranc 1 | 0.9627 $ -taiwandollar 1 | 0.0306 $ -thailandbaht 1 | 0.0294 $ -turkeynewlira 1 | 0.6678 $ -uaedirham 1 | 0.2722 $ -uruguaynewpeso 1 | 0.0451 $ -vietnamdong 1 | 0.0001 $ +/ Wed Aug 29, 2012 + +argentinapeso $ 0.2160 +australiadollar $ 1.0372 +boliviaboliviano $ 0.1427 +brazilreal $ 0.4872 +britainpound $ 1.5843 +canadadollar $ 1.0117 +chilepeso $ 1 | 480.6 +chinayuan $ 0.1574 +colombiapeso $ 1 | 1834 +czechkoruna $ 0.0506 +denmarkkrone $ 0.1681 +dominicanpeso $ 0.0256 +egyptpound $ 0.1640 +elsalvadorcolon $ 1 | 8.75 +europeuro $ 1.2528 +guatemalaquetzal $ 0.1290 +honduraslempira $ 0.0511 +hongkongdollar $ 0.1289 +hungaryforint $ 1 | 226.5 +indiarupee $ 0.0180 +indonesiarupiah $ 1 | 9540 +israelshekel $ 0.2479 +japanyen $ 0.0127 +kenyashilling $ 0.0119 +kuwaitdinar $ 3.5456 +lebanonpound $ 1 | 1505.5 +malaysiaringgit $ 0.3204 +mexicopeso $ 0.0754 +newzealanddollar $ 0.8035 +nicaraguacordoba $ 0.0421 +norwaykrone $ 0.1717 +pakistanrupee $ 0.0106 +paraguayguarani $ 1 | 4415 +perunewsol $ 0.3832 +philippinespeso $ 0.0236 +polandzloty $ 0.3001 +russiaruble $ 0.0311 +saudiarabiariyal $ 1 | 3.75 +singaporedollar $ 0.7976 +slovakkoruna 1 | 30.126 europeuro +southafricarand $ 0.1188 +southkoreawon $ 1 | 1135 +swedenkrona $ 0.1502 +switzerlandfranc $ 1.0431 +taiwandollar $ 0.0334 +thailandbaht $ 0.0319 +turkeynewlira $ 0.5504 +uaedirham $ 0.2723 +uruguaynewpeso $ 0.0465 +vietnamdong $ 1 | 20865 / END MONEY diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y index 7258e3e59..9c1b0b336 100644 --- a/src/cmd/yacc/units.y +++ b/src/cmd/yacc/units.y @@ -11,6 +11,11 @@ %{ +// This tag will end up in the generated y.go, so that forgetting +// 'make clean' does not fail the next build. + +// +build ignore + // units.y // example of a Go yacc program // usage is @@ -26,11 +31,13 @@ package main import ( + "bufio" "flag" "fmt" - "bufio" - "os" "math" + "runtime" + "os" + "path/filepath" "strconv" "unicode/utf8" ) @@ -74,9 +81,9 @@ var vflag bool %type <node> prog expr expr0 expr1 expr2 expr3 expr4 -%token <vval> VAL +%token <vval> VÄL // dieresis to test UTF-8 %token <vvar> VAR -%token <numb> SUP +%token <numb> _SUP // tests leading underscore in token name %% prog: ':' VAR expr @@ -157,7 +164,7 @@ expr3: expr2: expr1 -| expr2 SUP +| expr2 _SUP { xpn(&$$, &$1, $2) } @@ -197,7 +204,7 @@ expr0: $$ = $1.node } } -| VAL +| VÄL { $$ = one $$.vval = $1 @@ -234,13 +241,13 @@ loop: return '/' case '¹', 'ⁱ': yylval.numb = 1 - return SUP + return _SUP case '²', '': yylval.numb = 2 - return SUP + return _SUP case '³', '': yylval.numb = 3 - return SUP + return _SUP } return int(c) @@ -273,7 +280,7 @@ numb: f = 0 } yylval.vval = f - return VAL + return VÄL } func (UnitsLex) Error(s string) { @@ -287,13 +294,11 @@ func main() { flag.Parse() - if dir := os.Getenv("GOROOT"); dir != "" { - file = dir + "/src/cmd/yacc/units.txt" - } + file = filepath.Join(runtime.GOROOT(), "src/cmd/yacc/units.txt") if flag.NArg() > 0 { file = flag.Arg(0) } else if file == "" { - fmt.Fprintf(os.Stderr, "can not find data file units.txt; provide it as argument or set $GOROOT\n") + fmt.Fprintf(os.Stderr, "cannot find data file units.txt; provide it as argument or set $GOROOT\n") os.Exit(1) } @@ -308,7 +313,7 @@ func main() { /* * read the 'units' file to - * develope a database + * develop a database */ lineno = 0 for { diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index e94228152..0c18f93b6 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -51,6 +51,8 @@ import ( "fmt" "os" "strings" + "unicode" + "unicode/utf8" ) // the following are adjustable @@ -153,6 +155,8 @@ var ftable *bufio.Writer // y.go file var fcode = &bytes.Buffer{} // saved code var foutput *bufio.Writer // y.output file +var fmtImported bool // output file has recorded an import of "fmt" + var oflag string // -o [y.go] - y.go file var vflag string // -v [y.output] - y.output file var lflag bool // -l - disable line directives @@ -323,7 +327,6 @@ var resrv = []Resrv{ var zznewstate = 0 const EOF = -1 -const UTFmax = 0x3f func main() { @@ -716,8 +719,8 @@ func moreprod() { } // -// define s to be a terminal if t=0 -// or a nonterminal if t=1 +// define s to be a terminal if nt==0 +// or a nonterminal if nt==1 // func defin(nt int, s string) int { val := 0 @@ -750,56 +753,66 @@ func defin(nt int, s string) int { // establish value for token // single character literal - if s[0] == ' ' && len(s) == 1+1 { - val = int(s[1]) - } else if s[0] == ' ' && s[1] == '\\' { // escape sequence - if len(s) == 2+1 { - // single character escape sequence - switch s[2] { - case '\'': - val = '\'' - case '"': - val = '"' - case '\\': - val = '\\' - case 'a': - val = '\a' - case 'b': - val = '\b' - case 'n': - val = '\n' - case 'r': - val = '\r' - case 't': - val = '\t' - case 'v': - val = '\v' - default: - errorf("invalid escape %v", s[1:3]) - } - } else if s[2] == 'u' && len(s) == 2+1+4 { // \unnnn sequence - val = 0 - s = s[3:] - for s != "" { - c := int(s[0]) - switch { - case c >= '0' && c <= '9': - c -= '0' - case c >= 'a' && c <= 'f': - c -= 'a' - 10 - case c >= 'A' && c <= 'F': - c -= 'A' - 10 + if s[0] == ' ' { + s = s[1:] + r, size := utf8.DecodeRuneInString(s) + if r == utf8.RuneError && size == 1 { + errorf("invalid UTF-8 sequence %q", s) + } + val = int(r) + if val == '\\' { // escape sequence + switch { + case len(s) == 2: + // single character escape sequence + switch s[1] { + case '\'': + val = '\'' + case '"': + val = '"' + case '\\': + val = '\\' + case 'a': + val = '\a' + case 'b': + val = '\b' + case 'f': + val = '\f' + case 'n': + val = '\n' + case 'r': + val = '\r' + case 't': + val = '\t' + case 'v': + val = '\v' default: - errorf("illegal \\unnnn construction") + errorf("invalid escape %s", s) } - val = val*16 + c - s = s[1:] - } - if val == 0 { - errorf("'\\u0000' is illegal") + case s[1] == 'u' && len(s) == 2+4, // \unnnn sequence + s[1] == 'U' && len(s) == 2+8: // \Unnnnnnnn sequence + val = 0 + s = s[2:] + for s != "" { + c := int(s[0]) + switch { + case c >= '0' && c <= '9': + c -= '0' + case c >= 'a' && c <= 'f': + c -= 'a' - 10 + case c >= 'A' && c <= 'F': + c -= 'A' - 10 + default: + errorf(`illegal \u or \U construction`) + } + val = val*16 + c + s = s[1:] + } + default: + errorf("invalid escape %s", s) } - } else { - errorf("unknown escape") + } + if val == 0 { + errorf("token value 0 is illegal") } } else { val = extval @@ -985,7 +998,7 @@ func gettok() int { func getword(c rune) { tokname = "" - for isword(c) || isdigit(c) || c == '_' || c == '.' || c == '$' { + for isword(c) || isdigit(c) || c == '.' || c == '$' { tokname += string(c) c = getrune(finput) } @@ -1073,6 +1086,7 @@ out: // // saves code between %{ and %} +// adds an import for __fmt__ the first time // func cpycode() { lno := lineno @@ -1085,15 +1099,18 @@ func cpycode() { if !lflag { fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) } + // accumulate until %} + code := make([]rune, 0, 1024) for c != EOF { if c == '%' { c = getrune(finput) if c == '}' { + emitcode(code, lno+1) return } - ftable.WriteRune('%') + code = append(code, '%') } - ftable.WriteRune(c) + code = append(code, c) if c == '\n' { lineno++ } @@ -1104,6 +1121,107 @@ func cpycode() { } // +// emits code saved up from between %{ and %} +// called by cpycode +// adds an import for __yyfmt__ after the package clause +// +func emitcode(code []rune, lineno int) { + for i, line := range lines(code) { + writecode(line) + if !fmtImported && isPackageClause(line) { + fmt.Fprintln(ftable, `import __yyfmt__ "fmt"`) + fmt.Fprintf(ftable, "//line %v:%v\n\t\t", infile, lineno+i) + fmtImported = true + } + } +} + +// +// does this line look like a package clause? not perfect: might be confused by early comments. +// +func isPackageClause(line []rune) bool { + line = skipspace(line) + + // must be big enough. + if len(line) < len("package X\n") { + return false + } + + // must start with "package" + for i, r := range []rune("package") { + if line[i] != r { + return false + } + } + line = skipspace(line[len("package"):]) + + // must have another identifier. + if len(line) == 0 || (!unicode.IsLetter(line[0]) && line[0] != '_') { + return false + } + for len(line) > 0 { + if !unicode.IsLetter(line[0]) && !unicode.IsDigit(line[0]) && line[0] != '_' { + break + } + line = line[1:] + } + line = skipspace(line) + + // eol, newline, or comment must follow + if len(line) == 0 { + return true + } + if line[0] == '\r' || line[0] == '\n' { + return true + } + if len(line) >= 2 { + return line[0] == '/' && (line[1] == '/' || line[1] == '*') + } + return false +} + +// +// skip initial spaces +// +func skipspace(line []rune) []rune { + for len(line) > 0 { + if line[0] != ' ' && line[0] != '\t' { + break + } + line = line[1:] + } + return line +} + +// +// break code into lines +// +func lines(code []rune) [][]rune { + l := make([][]rune, 0, 100) + for len(code) > 0 { + // one line per loop + var i int + for i = range code { + if code[i] == '\n' { + break + } + } + l = append(l, code[:i+1]) + code = code[i+1:] + } + return l +} + +// +// writes code to ftable +// +func writecode(code []rune) { + for _, r := range code { + ftable.WriteRune(r) + } +} + +// // skip over comments // skipcom is called after reading a '/' // @@ -1230,7 +1348,7 @@ loop: if j >= max { errorf("Illegal use of $%v", j) } - } else if isword(c) || c == '_' || c == '.' { + } else if isword(c) || c == '.' { // look for $name ungetrune(finput, c) if gettok() != IDENTIFIER { @@ -2775,7 +2893,7 @@ func others() { j = tokset[i].value if j >= 0 && j < 256 { if temp1[j] != 0 { - fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n") fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) nerrors++ } @@ -2799,7 +2917,7 @@ func others() { j = tokset[i].value - PRIVATE if j >= 0 && j < 256 { if temp1[j] != 0 { - fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Print("yacc bug -- cannot have 2 different Ts with same value\n") fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) nerrors++ } @@ -2982,7 +3100,7 @@ var peekrune rune func isdigit(c rune) bool { return c >= '0' && c <= '9' } func isword(c rune) bool { - return c >= 0xa0 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') } func mktemp(t string) string { return t } @@ -3110,12 +3228,13 @@ type $$Lexer interface { const $$Flag = -1000 func $$Tokname(c int) string { - if c > 0 && c <= len($$Toknames) { - if $$Toknames[c-1] != "" { - return $$Toknames[c-1] + // 4 is TOKSTART above + if c >= 4 && c-4 < len($$Toknames) { + if $$Toknames[c-4] != "" { + return $$Toknames[c-4] } } - return fmt.Sprintf("tok-%v", c) + return __yyfmt__.Sprintf("tok-%v", c) } func $$Statname(s int) string { @@ -3124,7 +3243,7 @@ func $$Statname(s int) string { return $$Statenames[s] } } - return fmt.Sprintf("state-%v", s) + return __yyfmt__.Sprintf("state-%v", s) } func $$lex1(lex $$Lexer, lval *$$SymType) int { @@ -3157,7 +3276,7 @@ out: c = $$Tok2[1] /* unknown char */ } if $$Debug >= 3 { - fmt.Printf("lex %U %s\n", uint(char), $$Tokname(c)) + __yyfmt__.Printf("lex %U %s\n", uint(char), $$Tokname(c)) } return c } @@ -3184,7 +3303,7 @@ ret1: $$stack: /* put a state and value onto the stack */ if $$Debug >= 4 { - fmt.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) + __yyfmt__.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) } $$p++ @@ -3253,8 +3372,8 @@ $$default: $$lex.Error("syntax error") Nerrs++ if $$Debug >= 1 { - fmt.Printf("%s", $$Statname($$state)) - fmt.Printf("saw %s\n", $$Tokname($$char)) + __yyfmt__.Printf("%s", $$Statname($$state)) + __yyfmt__.Printf("saw %s\n", $$Tokname($$char)) } fallthrough @@ -3273,7 +3392,7 @@ $$default: /* the current p has no shift on "error", pop stack */ if $$Debug >= 2 { - fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys) + __yyfmt__.Printf("error recovery pops state %d\n", $$S[$$p].yys) } $$p-- } @@ -3282,7 +3401,7 @@ $$default: case 3: /* no shift yet; clobber input char */ if $$Debug >= 2 { - fmt.Printf("error recovery discards %s\n", $$Tokname($$char)) + __yyfmt__.Printf("error recovery discards %s\n", $$Tokname($$char)) } if $$char == $$EofCode { goto ret1 @@ -3294,7 +3413,7 @@ $$default: /* reduction by production $$n */ if $$Debug >= 2 { - fmt.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) + __yyfmt__.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) } $$nt := $$n |