diff options
| author | Russ Cox <rsc@golang.org> | 2015-02-13 12:50:23 -0500 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2015-02-13 12:51:56 -0500 |
| commit | 87de9ce212988c8bdf0630750e772d8805091bcc (patch) | |
| tree | 0bb1cc671417e9b851d35a4bbcd4d756e5aee4e9 /src | |
| parent | 01925bd3f306c899cddfa59aa2ad41c9b77fcd74 (diff) | |
| parent | 5f1efe738be296cdbc586348af92eab621d068f5 (diff) | |
| download | go-87de9ce212988c8bdf0630750e772d8805091bcc.tar.xz | |
[dev.cc] all: merge master (5f1efe7) into dev.cc
Conflicts:
src/cmd/dist/build.go
Change-Id: I98a4b5e010bee91507b85bb8efd9c74e1a1f649c
Diffstat (limited to 'src')
268 files changed, 14391 insertions, 13130 deletions
diff --git a/src/cmd/5a/a.y b/src/cmd/5a/a.y index 429f7437c6..10e9f6feea 100644 --- a/src/cmd/5a/a.y +++ b/src/cmd/5a/a.y @@ -460,20 +460,32 @@ fcon: reglist: spreg { - $$ = 1 << $1; + if($1 < REG_R0 || $1 > REG_R15) + yyerror("invalid register in reglist"); + + $$ = 1 << ($1&15); } | spreg '-' spreg { int i; + + if($1 < REG_R0 || $1 > REG_R15) + yyerror("invalid register in reglist"); + if($3 < REG_R0 || $3 > REG_R15) + yyerror("invalid register in reglist"); + $$=0; for(i=$1; i<=$3; i++) - $$ |= 1<<i; + $$ |= 1<<(i&15); for(i=$3; i<=$1; i++) - $$ |= 1<<i; + $$ |= 1<<(i&15); } | spreg comma reglist { - $$ = (1<<$1) | $3; + if($1 < REG_R0 || $1 > REG_R15) + yyerror("invalid register in reglist"); + + $$ = (1<<($1&15)) | $3; } gen: diff --git a/src/cmd/5a/y.tab.c b/src/cmd/5a/y.tab.c index d9af383d78..416af9a321 100644 --- a/src/cmd/5a/y.tab.c +++ b/src/cmd/5a/y.tab.c @@ -580,14 +580,14 @@ static const yytype_uint16 yyrline[] = 220, 232, 237, 249, 260, 267, 274, 278, 282, 286, 293, 315, 323, 332, 339, 348, 359, 365, 368, 372, 377, 378, 381, 387, 398, 405, 412, 419, 427, 433, - 438, 444, 447, 453, 461, 465, 474, 480, 481, 482, - 483, 488, 494, 500, 506, 507, 510, 511, 519, 528, - 529, 538, 539, 545, 548, 549, 550, 552, 560, 568, - 577, 583, 589, 595, 603, 609, 617, 618, 622, 630, - 631, 637, 638, 646, 647, 650, 656, 664, 672, 680, - 690, 693, 697, 703, 704, 705, 708, 709, 713, 717, - 721, 725, 731, 734, 740, 741, 745, 749, 753, 757, - 761, 765, 769, 773, 777 + 438, 444, 447, 453, 461, 468, 483, 492, 493, 494, + 495, 500, 506, 512, 518, 519, 522, 523, 531, 540, + 541, 550, 551, 557, 560, 561, 562, 564, 572, 580, + 589, 595, 601, 607, 615, 621, 629, 630, 634, 642, + 643, 649, 650, 658, 659, 662, 668, 676, 684, 692, + 702, 705, 709, 715, 716, 717, 720, 721, 725, 729, + 733, 737, 743, 746, 752, 753, 757, 761, 765, 769, + 773, 777, 781, 785, 789 }; #endif @@ -2223,31 +2223,43 @@ yyreduce: case 64: #line 462 "a.y" { - (yyval.lval) = 1 << (yyvsp[(1) - (1)].lval); + if((yyvsp[(1) - (1)].lval) < REG_R0 || (yyvsp[(1) - (1)].lval) > REG_R15) + yyerror("invalid register in reglist"); + + (yyval.lval) = 1 << ((yyvsp[(1) - (1)].lval)&15); } break; case 65: -#line 466 "a.y" +#line 469 "a.y" { int i; + + if((yyvsp[(1) - (3)].lval) < REG_R0 || (yyvsp[(1) - (3)].lval) > REG_R15) + yyerror("invalid register in reglist"); + if((yyvsp[(3) - (3)].lval) < REG_R0 || (yyvsp[(3) - (3)].lval) > REG_R15) + yyerror("invalid register in reglist"); + (yyval.lval)=0; for(i=(yyvsp[(1) - (3)].lval); i<=(yyvsp[(3) - (3)].lval); i++) - (yyval.lval) |= 1<<i; + (yyval.lval) |= 1<<(i&15); for(i=(yyvsp[(3) - (3)].lval); i<=(yyvsp[(1) - (3)].lval); i++) - (yyval.lval) |= 1<<i; + (yyval.lval) |= 1<<(i&15); } break; case 66: -#line 475 "a.y" +#line 484 "a.y" { - (yyval.lval) = (1<<(yyvsp[(1) - (3)].lval)) | (yyvsp[(3) - (3)].lval); + if((yyvsp[(1) - (3)].lval) < REG_R0 || (yyvsp[(1) - (3)].lval) > REG_R15) + yyerror("invalid register in reglist"); + + (yyval.lval) = (1<<((yyvsp[(1) - (3)].lval)&15)) | (yyvsp[(3) - (3)].lval); } break; case 70: -#line 484 "a.y" +#line 496 "a.y" { (yyval.addr) = (yyvsp[(1) - (4)].addr); (yyval.addr).reg = (yyvsp[(3) - (4)].lval); @@ -2255,7 +2267,7 @@ yyreduce: break; case 71: -#line 489 "a.y" +#line 501 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REG; @@ -2264,7 +2276,7 @@ yyreduce: break; case 72: -#line 495 "a.y" +#line 507 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REG; @@ -2273,7 +2285,7 @@ yyreduce: break; case 73: -#line 501 "a.y" +#line 513 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2282,7 +2294,7 @@ yyreduce: break; case 77: -#line 512 "a.y" +#line 524 "a.y" { (yyval.addr) = (yyvsp[(1) - (1)].addr); if((yyvsp[(1) - (1)].addr).name != NAME_EXTERN && (yyvsp[(1) - (1)].addr).name != NAME_STATIC) { @@ -2291,7 +2303,7 @@ yyreduce: break; case 78: -#line 520 "a.y" +#line 532 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2301,7 +2313,7 @@ yyreduce: break; case 80: -#line 530 "a.y" +#line 542 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2311,7 +2323,7 @@ yyreduce: break; case 82: -#line 540 "a.y" +#line 552 "a.y" { (yyval.addr) = (yyvsp[(1) - (4)].addr); (yyval.addr).type = TYPE_MEM; @@ -2320,7 +2332,7 @@ yyreduce: break; case 87: -#line 553 "a.y" +#line 565 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_CONST; @@ -2329,7 +2341,7 @@ yyreduce: break; case 88: -#line 561 "a.y" +#line 573 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REG; @@ -2338,7 +2350,7 @@ yyreduce: break; case 89: -#line 569 "a.y" +#line 581 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REGREG; @@ -2348,7 +2360,7 @@ yyreduce: break; case 90: -#line 578 "a.y" +#line 590 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_SHIFT; @@ -2357,7 +2369,7 @@ yyreduce: break; case 91: -#line 584 "a.y" +#line 596 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_SHIFT; @@ -2366,7 +2378,7 @@ yyreduce: break; case 92: -#line 590 "a.y" +#line 602 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_SHIFT; @@ -2375,7 +2387,7 @@ yyreduce: break; case 93: -#line 596 "a.y" +#line 608 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_SHIFT; @@ -2384,7 +2396,7 @@ yyreduce: break; case 94: -#line 604 "a.y" +#line 616 "a.y" { if((yyval.lval) < REG_R0 || (yyval.lval) > REG_R15) print("register value out of range in shift\n"); @@ -2393,7 +2405,7 @@ yyreduce: break; case 95: -#line 610 "a.y" +#line 622 "a.y" { if((yyval.lval) < 0 || (yyval.lval) >= 32) print("shift value out of range\n"); @@ -2402,14 +2414,14 @@ yyreduce: break; case 97: -#line 619 "a.y" +#line 631 "a.y" { (yyval.lval) = REGPC; } break; case 98: -#line 623 "a.y" +#line 635 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range in R(...)\n"); @@ -2418,14 +2430,14 @@ yyreduce: break; case 100: -#line 632 "a.y" +#line 644 "a.y" { (yyval.lval) = REGSP; } break; case 102: -#line 639 "a.y" +#line 651 "a.y" { if((yyvsp[(3) - (4)].lval) < 0 || (yyvsp[(3) - (4)].lval) >= NREG) print("register value out of range in C(...)\n"); @@ -2434,7 +2446,7 @@ yyreduce: break; case 105: -#line 651 "a.y" +#line 663 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REG; @@ -2443,7 +2455,7 @@ yyreduce: break; case 106: -#line 657 "a.y" +#line 669 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_REG; @@ -2452,7 +2464,7 @@ yyreduce: break; case 107: -#line 665 "a.y" +#line 677 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2463,7 +2475,7 @@ yyreduce: break; case 108: -#line 673 "a.y" +#line 685 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2474,7 +2486,7 @@ yyreduce: break; case 109: -#line 681 "a.y" +#line 693 "a.y" { (yyval.addr) = nullgen; (yyval.addr).type = TYPE_MEM; @@ -2485,140 +2497,140 @@ yyreduce: break; case 110: -#line 690 "a.y" +#line 702 "a.y" { (yyval.lval) = 0; } break; case 111: -#line 694 "a.y" +#line 706 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; case 112: -#line 698 "a.y" +#line 710 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; case 117: -#line 710 "a.y" +#line 722 "a.y" { (yyval.lval) = (yyvsp[(1) - (1)].sym)->value; } break; case 118: -#line 714 "a.y" +#line 726 "a.y" { (yyval.lval) = -(yyvsp[(2) - (2)].lval); } break; case 119: -#line 718 "a.y" +#line 730 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; case 120: -#line 722 "a.y" +#line 734 "a.y" { (yyval.lval) = ~(yyvsp[(2) - (2)].lval); } break; case 121: -#line 726 "a.y" +#line 738 "a.y" { (yyval.lval) = (yyvsp[(2) - (3)].lval); } break; case 122: -#line 731 "a.y" +#line 743 "a.y" { (yyval.lval) = 0; } break; case 123: -#line 735 "a.y" +#line 747 "a.y" { (yyval.lval) = (yyvsp[(2) - (2)].lval); } break; case 125: -#line 742 "a.y" +#line 754 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) + (yyvsp[(3) - (3)].lval); } break; case 126: -#line 746 "a.y" +#line 758 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) - (yyvsp[(3) - (3)].lval); } break; case 127: -#line 750 "a.y" +#line 762 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) * (yyvsp[(3) - (3)].lval); } break; case 128: -#line 754 "a.y" +#line 766 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) / (yyvsp[(3) - (3)].lval); } break; case 129: -#line 758 "a.y" +#line 770 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) % (yyvsp[(3) - (3)].lval); } break; case 130: -#line 762 "a.y" +#line 774 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) << (yyvsp[(4) - (4)].lval); } break; case 131: -#line 766 "a.y" +#line 778 "a.y" { (yyval.lval) = (yyvsp[(1) - (4)].lval) >> (yyvsp[(4) - (4)].lval); } break; case 132: -#line 770 "a.y" +#line 782 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) & (yyvsp[(3) - (3)].lval); } break; case 133: -#line 774 "a.y" +#line 786 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) ^ (yyvsp[(3) - (3)].lval); } break; case 134: -#line 778 "a.y" +#line 790 "a.y" { (yyval.lval) = (yyvsp[(1) - (3)].lval) | (yyvsp[(3) - (3)].lval); } @@ -2626,7 +2638,7 @@ yyreduce: /* Line 1267 of yacc.c. */ -#line 2630 "y.tab.c" +#line 2642 "y.tab.c" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index 6acf6dfdf0..354e0cbfd6 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -1148,27 +1148,20 @@ bgen(Node *n, int true, int likely, Prog *to) goto ret; case OANDAND: - if(!true) - goto caseor; - - caseand: - p1 = gbranch(AB, T, 0); - p2 = gbranch(AB, T, 0); - patch(p1, pc); - 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; - case OOROR: - if(!true) - goto caseand; - - caseor: - bgen(n->left, true, likely, to); - bgen(n->right, true, likely, to); + if((n->op == OANDAND) == true) { + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); + patch(p1, to); + patch(p2, pc); + } else { + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); + } goto ret; case OEQ: @@ -1626,6 +1619,8 @@ cadable(Node *n) /* * 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. */ @@ -1680,7 +1675,7 @@ componentgen(Node *nr, Node *nl) nodl = *nl; if(!cadable(nl)) { - if(nr == N || !cadable(nr)) + if(nr != N && !cadable(nr)) goto no; igen(nl, &nodl, N); freel = 1; @@ -1700,7 +1695,6 @@ componentgen(Node *nr, Node *nl) freer = 1; } - // nl and nr are 'cadable' which basically means they are names (variables) now. // If they are the same variable, don't generate any code, because the // VARDEF we generate will mark the old value as dead incorrectly. diff --git a/src/cmd/5g/galign.c b/src/cmd/5g/galign.c index 85ad347833..c4d74f0a71 100644 --- a/src/cmd/5g/galign.c +++ b/src/cmd/5g/galign.c @@ -42,61 +42,46 @@ betypeinit(void) void main(int argc, char **argv) { - arch.thechar = thechar; - arch.thestring = thestring; - arch.thelinkarch = thelinkarch; - arch.typedefs = typedefs; - arch.MAXWIDTH = MAXWIDTH; - arch.afunclit = afunclit; - arch.anyregalloc = anyregalloc; - arch.betypeinit = betypeinit; - arch.bgen = bgen; - arch.cgen = cgen; - arch.cgen_asop = cgen_asop; - arch.cgen_call = cgen_call; - arch.cgen_callinter = cgen_callinter; - arch.cgen_ret = cgen_ret; - arch.clearfat = clearfat; - arch.clearp = clearp; - arch.defframe = defframe; - arch.dgostringptr = dgostringptr; - arch.dgostrlitptr = dgostrlitptr; - arch.dsname = dsname; - arch.dsymptr = dsymptr; - arch.dumpdata = dumpdata; - arch.dumpit = dumpit; - arch.excise = excise; - arch.expandchecks = expandchecks; - arch.fixautoused = fixautoused; - arch.gclean = gclean; - arch.gdata = gdata; - arch.gdatacomplex = gdatacomplex; - arch.gdatastring = gdatastring; - arch.ggloblnod = ggloblnod; - arch.ggloblsym = ggloblsym; - arch.ginit = ginit; - arch.gins = gins; - arch.ginscall = ginscall; - arch.gjmp = gjmp; - arch.gtrack = gtrack; - arch.gused = gused; - arch.igen = igen; - arch.isfat = isfat; - arch.linkarchinit = linkarchinit; - arch.markautoused = markautoused; - arch.naddr = naddr; - arch.newplist = newplist; - arch.nodarg = nodarg; - arch.patch = patch; - arch.proginfo = proginfo; - arch.regalloc = regalloc; - arch.regfree = regfree; - arch.regopt = regopt; - arch.regtyp = regtyp; - arch.sameaddr = sameaddr; - arch.smallindir = smallindir; - arch.stackaddr = stackaddr; - arch.unpatch = unpatch; + thearch.thechar = thechar; + thearch.thestring = thestring; + thearch.thelinkarch = thelinkarch; + thearch.typedefs = typedefs; + thearch.REGSP = REGSP; + thearch.REGCTXT = REGCTXT; + thearch.MAXWIDTH = MAXWIDTH; + thearch.anyregalloc = anyregalloc; + thearch.betypeinit = betypeinit; + thearch.bgen = bgen; + thearch.cgen = cgen; + thearch.cgen_call = cgen_call; + thearch.cgen_callinter = cgen_callinter; + thearch.cgen_ret = cgen_ret; + thearch.clearfat = clearfat; + thearch.defframe = defframe; + thearch.excise = excise; + thearch.expandchecks = expandchecks; + thearch.gclean = gclean; + thearch.ginit = ginit; + thearch.gins = gins; + thearch.ginscall = ginscall; + thearch.igen = igen; + thearch.linkarchinit = linkarchinit; + thearch.peep = peep; + thearch.proginfo = proginfo; + thearch.regalloc = regalloc; + thearch.regfree = regfree; + thearch.regtyp = regtyp; + thearch.sameaddr = sameaddr; + thearch.smallindir = smallindir; + thearch.stackaddr = stackaddr; + thearch.excludedregs = excludedregs; + thearch.RtoB = RtoB; + thearch.FtoB = RtoB; + thearch.BtoR = BtoR; + thearch.BtoF = BtoF; + thearch.optoas = optoas; + thearch.doregbits = doregbits; + thearch.regnames = regnames; gcmain(argc, argv); } diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index 8a75311d73..b12c7e2561 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -17,10 +17,7 @@ enum REGALLOC_FMAX = FREGEXT, }; -EXTERN int32 dynloc; EXTERN uchar reg[REGALLOC_FMAX+1]; -EXTERN int32 pcloc; // instruction counter -EXTERN Strlit emptystring; extern long unmappedzero; /* @@ -118,7 +115,6 @@ int anyregalloc(void); void betypeinit(void); void bgen(Node*, int, int, Prog*); void cgen(Node*, Node*); -void cgen_asop(Node*); void cgen_call(Node*, int); void cgen_callinter(Node*, Node*, int); void cgen_ret(Node*); @@ -163,3 +159,19 @@ int sameaddr(Addr*, Addr*); int smallindir(Addr*, Addr*); int stackaddr(Addr*); Prog* unpatch(Prog*); + +/* + * reg.c + */ +uint64 excludedregs(void); +uint64 RtoB(int); +uint64 FtoB(int); +int BtoR(uint64); +int BtoF(uint64); +uint64 doregbits(int); +char** regnames(int*); + +/* + * peep.c + */ +void peep(Prog*); diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index b7c621be32..62b9beadb0 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -7,7 +7,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static Prog* appendpp(Prog*, int, int, int, int32, int, int, int32); static Prog *zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *r0); @@ -117,52 +117,6 @@ appendpp(Prog *p, int as, int ftype, int freg, int32 foffset, int ttype, int tre return q; } -// Sweep the prog list to mark any used nodes. -void -markautoused(Prog* p) -{ - for (; p; p = p->link) { - if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL) - continue; - - if (p->from.node) - ((Node*)(p->from.node))->used = 1; - - if (p->to.node) - ((Node*)(p->to.node))->used = 1; - } -} - -// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. -void -fixautoused(Prog* p) -{ - Prog **lp; - - for (lp=&p; (p=*lp) != P; ) { - if (p->as == ATYPE && p->from.node && p->from.name == NAME_AUTO && !((Node*)(p->from.node))->used) { - *lp = p->link; - continue; - } - if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !((Node*)(p->to.node))->used) { - // Cannot remove VARDEF instruction, because - unlike TYPE handled above - - // VARDEFs are interspersed with other code, and a jump might be using the - // VARDEF as a target. Replace with a no-op instead. A later pass will remove - // the no-ops. - nopout(p); - continue; - } - - if (p->from.name == NAME_AUTO && p->from.node) - p->from.offset += ((Node*)(p->from.node))->stkdelta; - - if (p->to.name == NAME_AUTO && p->to.node) - p->to.offset += ((Node*)(p->to.node))->stkdelta; - - lp = &p->link; - } -} - /* * generate: * call f @@ -176,7 +130,7 @@ void ginscall(Node *f, int proc) { Prog *p; - Node n1, r, r1, con; + Node r, r1, con; int32 extra; if(f->type != T) { @@ -238,10 +192,7 @@ ginscall(Node *f, int proc) p->to.reg = REGSP; p->to.offset = 4; - memset(&n1, 0, sizeof n1); - n1.op = OADDR; - n1.left = f; - gins(AMOVW, &n1, &r); + gins(AMOVW, f, &r); p = gins(AMOVW, &r, N); p->to.type = TYPE_MEM; p->to.reg = REGSP; @@ -487,147 +438,6 @@ cgen_ret(Node *n) } /* - * generate += *= etc. - */ -void -cgen_asop(Node *n) -{ - Node n1, n2, n3, n4; - Node *nl, *nr; - Prog *p1; - Addr addr; - int a, w; - - nl = n->left; - nr = n->right; - - if(nr->ullman >= UINF && nl->ullman >= UINF) { - tempname(&n1, nr->type); - cgen(nr, &n1); - n2 = *n; - n2.right = &n1; - cgen_asop(&n2); - goto ret; - } - - if(!isint[nl->type->etype]) - goto hard; - if(!isint[nr->type->etype]) - goto hard; - if(is64(nl->type) || is64(nr->type)) - goto hard64; - - switch(n->etype) { - case OADD: - case OSUB: - case OXOR: - case OAND: - case OOR: - a = optoas(n->etype, nl->type); - if(nl->addable) { - 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); - if(n3.op != OLITERAL) - regfree(&n3); - goto ret; - } - if(nr->ullman < UINF) - if(sudoaddable(a, nl, &addr, &w)) { - w = optoas(OAS, nl->type); - regalloc(&n2, nl->type, N); - p1 = gins(w, N, &n2); - p1->from = addr; - regalloc(&n3, nr->type, N); - cgen(nr, &n3); - gins(a, &n3, &n2); - p1 = gins(w, &n2, N); - p1->to = addr; - regfree(&n2); - regfree(&n3); - sudoclean(); - goto ret; - } - } - -hard: - n2.op = 0; - n1.op = 0; - 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; - } else { - tempname(&n2, nr->type); - cgen(nr, &n2); - nr = &n2; - } - if(!nl->addable) { - igen(nl, &n1, N); - nl = &n1; - } - - n3 = *n; - n3.left = nl; - n3.right = nr; - n3.op = n->etype; - - regalloc(&n4, nl->type, N); - cgen(&n3, &n4); - gmove(&n4, nl); - - if(n1.op) - regfree(&n1); - if(n2.op == OREGISTER) - regfree(&n2); - regfree(&n4); - goto ret; - -hard64: - if(nr->ullman > nl->ullman) { - tempname(&n2, nr->type); - cgen(nr, &n2); - igen(nl, &n1, N); - } else { - igen(nl, &n1, N); - tempname(&n2, nr->type); - cgen(nr, &n2); - } - - n3 = *n; - n3.left = &n1; - n3.right = &n2; - n3.op = n->etype; - - cgen(&n3, &n1); - -ret: - ; -} - -int -samereg(Node *a, Node *b) -{ - if(a->op != OREGISTER) - return 0; - if(b->op != OREGISTER) - return 0; - if(a->val.u.reg != b->val.u.reg) - return 0; - return 1; -} - -/* * generate high multiply * res = (nl * nr) >> wordsize */ diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c deleted file mode 100644 index 13d06efe55..0000000000 --- a/src/cmd/5g/gobj.c +++ /dev/null @@ -1,267 +0,0 @@ -// Derived from Inferno utils/5c/swt.c -// http://code.google.com/p/inferno-os/source/browse/utils/5c/swt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - -#include <u.h> -#include <libc.h> -#include "gg.h" - -int -dsname(Sym *sym, int off, char *t, int n) -{ - Prog *p; - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.etype = TINT32; - p->from.offset = off; - p->from.reg = 0; - p->from.sym = linksym(sym); - - p->from3.type = TYPE_CONST; - p->from3.offset = n; - - p->to.type = TYPE_SCONST; - p->to.name = NAME_NONE; - p->to.reg = 0; - p->to.offset = 0; - memmove(p->to.u.sval, t, n); - return off + n; -} - -/* - * make a refer to the data s, s+len - * emitting DATA if needed. - */ -void -datastring(char *s, int len, Addr *a) -{ - Sym *sym; - - sym = stringsym(s, len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->etype = TINT32; - a->offset = widthptr+4; // skip header - a->reg = 0; - a->sym = linksym(sym); - a->node = sym->def; -} - -/* - * make a refer to the string sval, - * emitting DATA if needed. - */ -void -datagostring(Strlit *sval, Addr *a) -{ - Sym *sym; - - sym = stringsym(sval->s, sval->len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->etype = TSTRING; - a->offset = 0; // header - a->reg = 0; - a->sym = linksym(sym); - a->node = sym->def; -} - -void -gdata(Node *nam, Node *nr, int wid) -{ - Prog *p; - vlong v; - - if(nr->op == OLITERAL) { - switch(nr->val.ctype) { - case CTCPLX: - gdatacomplex(nam, nr->val.u.cval); - return; - case CTSTR: - gdatastring(nam, nr->val.u.sval); - return; - } - } - - if(wid == 8 && is64(nr->type)) { - v = mpgetfix(nr->val.u.xval); - p = gins(ADATA, nam, nodintconst(v)); - p->from3.type = TYPE_CONST; - p->from3.offset = 4; - p = gins(ADATA, nam, nodintconst(v>>32)); - p->from3.type = TYPE_CONST; - p->from3.offset = 4; - p->from.offset += 4; - return; - } - p = gins(ADATA, nam, nr); - p->from3.type = TYPE_CONST; - p->from3.offset = wid; -} - -void -gdatacomplex(Node *nam, Mpcplx *cval) -{ - Prog *p; - int w; - - w = cplxsubtype(nam->type->etype); - w = types[w]->width; - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->real); - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->from.offset += w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->imag); -} - -void -gdatastring(Node *nam, Strlit *sval) -{ - Prog *p; - Node nod1; - - p = gins(ADATA, nam, N); - datastring(sval->s, sval->len, &p->to); - p->from3.type = TYPE_CONST; - p->from3.offset = types[tptr]->width; - p->to.type = TYPE_CONST; - p->to.etype = TINT32; -//print("%P\n", p); - - nodconst(&nod1, types[TINT32], sval->len); - p = gins(ADATA, nam, &nod1); - p->from3.type = TYPE_CONST; - p->from3.offset = types[TINT32]->width; - p->from.offset += types[tptr]->width; -} - -int -dstringptr(Sym *s, int off, char *str) -{ - Prog *p; - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - - datastring(str, strlen(str)+1, &p->to); - p->to.type = TYPE_CONST; - p->to.etype = TINT32; - off += widthptr; - - return off; -} - -int -dgostrlitptr(Sym *s, int off, Strlit *lit) -{ - Prog *p; - - if(lit == nil) - return duintptr(s, off, 0); - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - datagostring(lit, &p->to); - p->to.type = TYPE_CONST; - p->to.etype = TINT32; - off += widthptr; - - return off; -} - -int -dgostringptr(Sym *s, int off, char *str) -{ - int n; - Strlit *lit; - - if(str == nil) - return duintptr(s, off, 0); - - n = strlen(str); - lit = mal(sizeof *lit + n); - strcpy(lit->s, str); - lit->len = n; - return dgostrlitptr(s, off, lit); -} - -int -dsymptr(Sym *s, int off, Sym *x, int xoff) -{ - Prog *p; - - off = rnd(off, widthptr); - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - p->to.type = TYPE_ADDR; - p->to.name = NAME_EXTERN; - p->to.sym = linksym(x); - p->to.offset = xoff; - off += widthptr; - - return off; -} - -void -nopout(Prog *p) -{ - p->as = ANOP; - p->scond = zprog.scond; - p->from = zprog.from; - p->to = zprog.to; - p->reg = zprog.reg; -} diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index ef5a509147..bdb70270a8 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -38,229 +38,6 @@ // At the same time, can raise StackBig in ../../runtime/stack.h. long unmappedzero = 4096; -void -clearp(Prog *p) -{ - p->as = AEND; - p->reg = 0; - p->scond = C_SCOND_NONE; - p->from.type = TYPE_NONE; - p->from.name = NAME_NONE; - p->from.reg = 0; - p->to.type = TYPE_NONE; - p->to.name = NAME_NONE; - p->to.reg = 0; - p->pc = pcloc; - pcloc++; -} - -static int ddumped; -static Prog *dfirst; -static Prog *dpc; - -/* - * generate and return proc with p->as = as, - * linked into program. pc is next instruction. - */ -Prog* -prog(int as) -{ - Prog *p; - - if(as == ADATA || as == AGLOBL) { - if(ddumped) - fatal("already dumped data"); - if(dpc == nil) { - dpc = mal(sizeof(*dpc)); - dfirst = dpc; - } - p = dpc; - dpc = mal(sizeof(*dpc)); - p->link = dpc; - p->reg = 0; // used for flags - } else { - p = pc; - pc = mal(sizeof(*pc)); - clearp(pc); - p->link = pc; - } - - if(lineno == 0) { - if(debug['K']) - warn("prog: line 0"); - } - - p->as = as; - p->lineno = lineno; - return p; -} - -void -dumpdata(void) -{ - ddumped = 1; - if(dfirst == nil) - return; - newplist(); - *pc = *dfirst; - pc = dpc; - clearp(pc); -} - -/* - * 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, int likely) -{ - Prog *p; - - USED(t); - USED(likely); // TODO: record this for linker - - p = prog(as); - p->to.type = TYPE_BRANCH; - p->to.u.branch = P; - return p; -} - -/* - * patch previous branch to jump to to. - */ -void -patch(Prog *p, Prog *to) -{ - if(p->to.type != TYPE_BRANCH) - fatal("patch: not a branch"); - p->to.u.branch = to; - p->to.offset = to->pc; -} - -Prog* -unpatch(Prog *p) -{ - Prog *q; - - if(p->to.type != TYPE_BRANCH) - fatal("unpatch: not a branch"); - q = p->to.u.branch; - p->to.u.branch = P; - p->to.offset = 0; - return q; -} - -/* - * start a new Prog list. - */ -Plist* -newplist(void) -{ - Plist *pl; - - pl = linknewplist(ctxt); - - pc = mal(sizeof(*pc)); - clearp(pc); - pl->firstpc = pc; - - return pl; -} - -void -gused(Node *n) -{ - gins(ANOP, n, N); // used -} - -Prog* -gjmp(Prog *to) -{ - Prog *p; - - p = gbranch(AB, T, 0); - if(to != P) - patch(p, to); - return p; -} - -void -ggloblnod(Node *nam) -{ - Prog *p; - - p = gins(AGLOBL, nam, N); - p->lineno = nam->lineno; - p->from.sym->gotype = linksym(ngotype(nam)); - p->to.sym = nil; - p->to.type = TYPE_CONST; - p->to.offset = nam->type->width; - if(nam->readonly) - p->from3.offset = RODATA; - if(nam->type != T && !haspointers(nam->type)) - p->from3.offset |= NOPTR; -} - -void -ggloblsym(Sym *s, int32 width, int8 flags) -{ - Prog *p; - - p = gins(AGLOBL, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->to.type = TYPE_CONST; - p->to.name = NAME_NONE; - p->to.offset = width; - p->from3.offset = flags; -} - -void -gtrack(Sym *s) -{ - Prog *p; - - p = gins(AUSEFIELD, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); -} - -int -isfat(Type *t) -{ - if(t != T) - switch(t->etype) { - case TSTRUCT: - case TARRAY: - case TSTRING: - case TINTER: // maybe remove later - return 1; - } - return 0; -} - -/* - * naddr of func generates code for address of func. - * if using opcode that can take address implicitly, - * call afunclit to fix up the argument. - * also fix up direct register references to be TYPE_MEM. - */ -void -afunclit(Addr *a, Node *n) -{ - if(a->type == TYPE_ADDR && a->name == NAME_EXTERN || a->type == TYPE_REG) { - a->type = TYPE_MEM; - if(n->op == ONAME) - a->sym = linksym(n->sym); - } -} - static int resvd[] = { 9, // reserved for m @@ -292,7 +69,7 @@ gclean(void) yyerror("reg %R left allocated\n", i); } -int32 +int anyregalloc(void) { int i, j; @@ -427,101 +204,6 @@ regfree(Node *n) } /* - * initialize n to be register r of type t. - */ -void -nodreg(Node *n, Type *t, int r) -{ - if(t == T) - fatal("nodreg: t nil"); - - memset(n, 0, sizeof(*n)); - n->op = OREGISTER; - n->addable = 1; - ullmancalc(n); - n->val.u.reg = r; - n->type = t; -} - -/* - * initialize n to be indirect of register r; n is type t. - */ -void -nodindreg(Node *n, Type *t, int r) -{ - nodreg(n, t, r); - n->op = OINDREG; -} - -Node* -nodarg(Type *t, int fp) -{ - Node *n; - NodeList *l; - Type *first; - Iter savet; - - // entire argument struct, not just one arg - if(t->etype == TSTRUCT && t->funarg) { - n = nod(ONAME, N, N); - n->sym = lookup(".args"); - n->type = t; - first = structfirst(&savet, &t); - if(first == nil) - fatal("nodarg: bad struct"); - if(first->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = first->width; - n->addable = 1; - goto fp; - } - - if(t->etype != TFIELD) - fatal("nodarg: not field %T", t); - - if(fp == 1) { - for(l=curfn->dcl; l; l=l->next) { - n = l->n; - if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym) - return n; - } - } - - n = nod(ONAME, N, N); - n->type = t->type; - n->sym = t->sym; - if(t->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = t->width; - n->addable = 1; - n->orig = t->nname; - -fp: - // Rewrite argument named _ to __, - // or else the assignment to _ will be - // discarded during code generation. - if(isblank(n)) - n->sym = lookup("__"); - - switch(fp) { - default: - fatal("nodarg %T %d", t, fp); - - case 0: // output arg for calling another function - n->op = OINDREG; - n->val.u.reg = REGSP; - n->xoffset += 4; - break; - - case 1: // input arg to current function - n->class = PPARAM; - break; - } - n->typecheck = 1; - return n; -} - -/* * return constant i node. * overwritten by next call, but useful in calls to gins. */ @@ -536,22 +218,6 @@ ncon(uint32 i) return &n; } -/* - * Is this node a memory operand? - */ -int -ismem(Node *n) -{ - switch(n->op) { - case OINDREG: - case ONAME: - case OPARAM: - case OCLOSUREVAR: - return 1; - } - return 0; -} - Node sclean[10]; int nsclean; @@ -573,22 +239,25 @@ split64(Node *n, Node *lo, Node *hi) nsclean++; switch(n->op) { default: - if(!dotaddable(n, &n1)) { - igen(n, &n1, N); - sclean[nsclean-1] = n1; - } - n = &n1; - goto common; - case ONAME: - if(n->class == PPARAMREF) { - cgen(n->heapaddr, &n1); - sclean[nsclean-1] = n1; - // fall through. + switch(n->op) { + default: + if(!dotaddable(n, &n1)) { + igen(n, &n1, N); + sclean[nsclean-1] = n1; + } n = &n1; + break; + case ONAME: + if(n->class == PPARAMREF) { + cgen(n->heapaddr, &n1); + sclean[nsclean-1] = n1; + n = &n1; + } + break; + case OINDREG: + // nothing + break; } - goto common; - case OINDREG: - common: *lo = *n; *hi = *n; lo->type = types[TUINT32]; @@ -1182,228 +851,6 @@ gregshift(int as, Node *lhs, int32 stype, Node *reg, Node *rhs) } /* - * generate code to compute n; - * make a refer to result. - */ -void -naddr(Node *n, Addr *a, int canemitcode) -{ - Sym *s; - - a->type = TYPE_NONE; - a->name = NAME_NONE; - a->reg = 0; - a->gotype = nil; - 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); - break; - - case OREGISTER: - a->type = TYPE_REG; - a->reg = n->val.u.reg; - a->sym = nil; - break; - - case OINDEX: - case OIND: - fatal("naddr: OINDEX"); -// naddr(n->left, a); -// if(a->type >= D_AX && a->type <= D_DI) -// a->type += D_INDIR; -// else -// if(a->type == TYPE_ADDR) -// a->type = TYPE_NONE+D_INDIR; -// else -// if(a->type == TYPE_ADDR) { -// a->type = a->index; -// a->index = TYPE_NONE; -// } else -// goto bad; -// if(n->op == OINDEX) { -// a->index = idx.reg; -// a->scale = n->scale; -// } -// break; - - case OINDREG: - a->type = TYPE_MEM; - a->reg = n->val.u.reg; - a->sym = linksym(n->sym); - a->offset = n->xoffset; - break; - - case OPARAM: - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - a->etype = simtype[n->left->type->etype]; - a->width = n->left->type->width; - a->offset = n->xoffset; - a->sym = linksym(n->left->sym); - a->type = TYPE_MEM; - a->name = NAME_PARAM; - a->node = n->left->orig; - break; - - case OCLOSUREVAR: - if(!curfn->needctxt) - fatal("closurevar without needctxt"); - a->type = TYPE_MEM; - a->reg = REG_R7; - a->offset = n->xoffset; - a->sym = nil; - break; - - case OCFUNC: - naddr(n->left, a, canemitcode); - a->sym = linksym(n->left->sym); - break; - - case ONAME: - a->etype = 0; - a->width = 0; - a->reg = 0; - if(n->type != T) { - a->etype = simtype[n->type->etype]; - a->width = n->type->width; - } - a->offset = n->xoffset; - s = n->sym; - a->node = n->orig; - //if(a->node >= (Node*)&n) - // fatal("stack node"); - if(s == S) - s = lookup(".noname"); - if(n->method) { - if(n->type != T) - if(n->type->sym != S) - if(n->type->sym->pkg != nil) - s = pkglookup(s->name, n->type->sym->pkg); - } - - a->type = TYPE_MEM; - switch(n->class) { - default: - fatal("naddr: ONAME class %S %d\n", n->sym, n->class); - case PEXTERN: - a->name = NAME_EXTERN; - break; - case PAUTO: - a->name = NAME_AUTO; - break; - case PPARAM: - case PPARAMOUT: - a->name = NAME_PARAM; - break; - case PFUNC: - a->name = NAME_EXTERN; - a->type = TYPE_ADDR; - s = funcsym(s); - break; - } - a->sym = linksym(s); - break; - - case OLITERAL: - switch(n->val.ctype) { - default: - fatal("naddr: const %lT", n->type); - break; - case CTFLT: - a->type = TYPE_FCONST; - a->u.dval = mpgetflt(n->val.u.fval); - break; - case CTINT: - case CTRUNE: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = mpgetfix(n->val.u.xval); - break; - case CTSTR: - datagostring(n->val.u.sval, a); - break; - case CTBOOL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = n->val.u.bval; - break; - case CTNIL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = 0; - break; - } - break; - - case OITAB: - // itable of interface value - naddr(n->left, a, canemitcode); - a->etype = simtype[tptr]; - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->width = widthptr; - break; - - case OSPTR: - // pointer in a string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // ptr(nil) - a->etype = simtype[tptr]; - a->offset += Array_array; - a->width = widthptr; - break; - - case OLEN: - // len of string or slice - naddr(n->left, a, canemitcode); - a->etype = TINT32; - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->offset += Array_nel; - break; - - case OCAP: - // cap of string or slice - naddr(n->left, a, canemitcode); - a->etype = TINT32; - if(a->type == TYPE_CONST && a->offset == 0) - break; // cap(nil) - a->offset += Array_cap; - break; - - case OADDR: - naddr(n->left, a, canemitcode); - a->etype = tptr; - switch(a->type) { - case TYPE_MEM: - a->type = TYPE_ADDR; - break; - - case TYPE_REG: - case TYPE_ADDR: - break; - - default: - fatal("naddr: OADDR %d\n", a->type); - } - } - - if(a->width < 0) - fatal("naddr: bad width for %N -> %D", n, a); -} - -/* * return Axxx for Oxxx on type t. */ int @@ -1812,6 +1259,8 @@ sudoaddable(int as, Node *n, Addr *a, int *w) if(n->type == T) return 0; + memset(a, 0, sizeof *a); + switch(n->op) { case OLITERAL: if(!isconst(n, CTINT)) diff --git a/src/cmd/5g/opt.h b/src/cmd/5g/opt.h deleted file mode 100644 index 524607419d..0000000000 --- a/src/cmd/5g/opt.h +++ /dev/null @@ -1,179 +0,0 @@ -// Inferno utils/5c/gc.h -// http://code.google.com/p/inferno-os/source/browse/utils/5c/gc.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - - -#define Z N -#define Adr Addr - -#define D_HI TYPE_NONE -#define D_LO TYPE_NONE - -#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]) -#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z]) - -#define CLOAD 5 -#define CREF 5 -#define CINF 1000 -#define LOOP 3 - -typedef struct Reg Reg; -typedef struct Rgn Rgn; - -/*c2go -extern Node *Z; -enum -{ - D_HI = TYPE_NONE, - D_LO = TYPE_NONE, - CLOAD = 5, - CREF = 5, - CINF = 1000, - LOOP = 3, -}; - -uint32 BLOAD(Reg*); -uint32 BSTORE(Reg*); -uint64 LOAD(Reg*); -uint64 STORE(Reg*); -*/ - -// 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->opt points back to r. -struct Reg -{ - Flow f; - - Bits set; // regopt variables written by this instruction. - Bits use1; // regopt variables read by prog->from. - Bits use2; // regopt variables read by prog->to. - - // refahead/refbehind are the regopt variables whose current - // value may be used in the following/preceding instructions - // up to a CALL (or the value is clobbered). - Bits refbehind; - Bits refahead; - // calahead/calbehind are similar, but for variables in - // instructions that are reachable after hitting at least one - // CALL. - Bits calbehind; - Bits calahead; - Bits regdiff; - Bits act; - - int32 regu; // register used bitmap -}; -#define R ((Reg*)0) -/*c2go extern Reg *R; */ - -#define NRGN 600 -/*c2go enum { NRGN = 600 }; */ - -// A Rgn represents a single regopt variable over a region of code -// where a register could potentially be dedicated to that variable. -// The code encompassed by a Rgn is defined by the flow graph, -// starting at enter, flood-filling forward while varno is refahead -// and backward while varno is refbehind, and following branches. A -// single variable may be represented by multiple disjoint Rgns and -// each Rgn may choose a different register for that variable. -// Registers are allocated to regions greedily in order of descending -// cost. -struct Rgn -{ - Reg* enter; - short cost; - short varno; - short regno; -}; - -EXTERN Reg zreg; -EXTERN Reg* freer; -EXTERN Reg** rpo2r; -EXTERN Rgn region[NRGN]; -EXTERN Rgn* rgp; -EXTERN int nregion; -EXTERN int nvar; -EXTERN int32 regbits; -EXTERN Bits externs; -EXTERN Bits params; -EXTERN Bits consts; -EXTERN Bits addrs; -EXTERN Bits ivar; -EXTERN Bits ovar; -EXTERN int change; -EXTERN int32 maxnr; -EXTERN int32* idom; - -EXTERN struct -{ - int32 ncvtreg; - int32 nspill; - int32 nreload; - int32 ndelmov; - int32 nvar; - int32 naddr; -} ostats; - -/* - * reg.c - */ -Reg* rega(void); -int rcmp(const void*, const void*); -void regopt(Prog*); -void addmove(Reg*, int, int, int); -Bits mkvar(Reg *r, Adr *a); -void prop(Reg*, Bits, Bits); -void synch(Reg*, Bits); -uint32 allreg(uint32, Rgn*); -void paint1(Reg*, int); -uint32 paint2(Reg*, int, int); -void paint3(Reg*, int, uint32, int); -void addreg(Adr*, int); -void dumpit(char *str, Flow *r0, int); - -/* - * peep.c - */ -void peep(Prog*); -void excise(Flow*); -int copyu(Prog*, Adr*, Adr*); - -uint32 RtoB(int); -uint32 FtoB(int); -int BtoR(uint32); -int BtoF(uint32); - -/* - * prog.c - */ -void proginfo(ProgInfo*, Prog*); diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c index 1a4df8d622..7fe3e1071f 100644 --- a/src/cmd/5g/peep.c +++ b/src/cmd/5g/peep.c @@ -32,7 +32,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static int xtramodes(Graph*, Flow*, Adr*); static int shortprop(Flow *r); @@ -47,6 +47,7 @@ static Flow* findpre(Flow *r, Adr *v); static int copyau1(Prog *p, Adr *v); static int isdconst(Addr *a); static int isfloatreg(Addr*); +static int copyu(Prog *p, Adr *v, Adr *s); static uint32 gactive; @@ -64,7 +65,7 @@ peep(Prog *firstp) Prog *p; int t; - g = flowstart(firstp, sizeof(Flow)); + g = flowstart(firstp, 0); if(g == nil) return; gactive = 0; @@ -121,15 +122,14 @@ loop1: } break; -#ifdef NOTDEF -XXX + /* if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) if(isdconst(&p->from)) { constprop(&p->from, &p->to, r->s1); } break; -#endif + */ } } if(t) @@ -561,8 +561,6 @@ gotit: * AXXX (x<<y),a,b * .. */ -#define FAIL(msg) { if(debug['P']) print("\t%s; FAILURE\n", msg); return 0; } -/*c2go void FAIL(char*); */ int shiftprop(Flow *r) @@ -573,8 +571,11 @@ shiftprop(Flow *r) Adr a; p = r->prog; - if(p->to.type != TYPE_REG) - FAIL("BOTCH: result not reg"); + if(p->to.type != TYPE_REG) { + if(debug['P']) + print("\tBOTCH: result not reg; FAILURE\n"); + return 0; + } n = p->to.reg; a = zprog.from; if(p->reg != 0 && p->reg != p->to.reg) { @@ -587,28 +588,43 @@ shiftprop(Flow *r) for(;;) { /* find first use of shift result; abort if shift operands or result are changed */ r1 = uniqs(r1); - if(r1 == nil) - FAIL("branch"); - if(uniqp(r1) == nil) - FAIL("merge"); + if(r1 == nil) { + if(debug['P']) + print("\tbranch; FAILURE\n"); + return 0; + } + if(uniqp(r1) == nil) { + if(debug['P']) + print("\tmerge; FAILURE\n"); + return 0; + } p1 = r1->prog; if(debug['P']) print("\n%P", p1); switch(copyu(p1, &p->to, nil)) { case 0: /* not used or set */ if((p->from.type == TYPE_REG && copyu(p1, &p->from, nil) > 1) || - (a.type == TYPE_REG && copyu(p1, &a, nil) > 1)) - FAIL("args modified"); + (a.type == TYPE_REG && copyu(p1, &a, nil) > 1)) { + if(debug['P']) + print("\targs modified; FAILURE\n"); + return 0; + } continue; - case 3: /* set, not used */ - FAIL("BOTCH: noref"); + case 3: /* set, not used */ { + if(debug['P']) + print("\tBOTCH: noref; FAILURE\n"); + return 0; + } } break; } /* check whether substitution can be done */ switch(p1->as) { default: - FAIL("non-dpi"); + if(debug['P']) + print("\tnon-dpi; FAILURE\n"); + return 0; + case AAND: case AEOR: case AADD: @@ -619,8 +635,11 @@ shiftprop(Flow *r) case ARSB: case ARSC: if(p1->reg == n || (p1->reg == 0 && p1->to.type == TYPE_REG && p1->to.reg == n)) { - if(p1->from.type != TYPE_REG) - FAIL("can't swap"); + if(p1->from.type != TYPE_REG) { + if(debug['P']) + print("\tcan't swap; FAILURE\n"); + return 0; + } p1->reg = p1->from.reg; p1->from.reg = n; switch(p1->as) { @@ -644,15 +663,27 @@ shiftprop(Flow *r) case ATST: case ACMP: case ACMN: - if(p1->reg == n) - FAIL("can't swap"); - if(p1->reg == 0 && p1->to.reg == n) - FAIL("shift result used twice"); + if(p1->reg == n) { + if(debug['P']) + print("\tcan't swap; FAILURE\n"); + return 0; + } + if(p1->reg == 0 && p1->to.reg == n) { + if(debug['P']) + print("\tshift result used twice; FAILURE\n"); + return 0; + } // case AMVN: - if(p1->from.type == TYPE_SHIFT) - FAIL("shift result used in shift"); - if(p1->from.type != TYPE_REG || p1->from.reg != n) - FAIL("BOTCH: where is it used?"); + if(p1->from.type == TYPE_SHIFT) { + if(debug['P']) + print("\tshift result used in shift; FAILURE\n"); + return 0; + } + if(p1->from.type != TYPE_REG || p1->from.reg != n) { + if(debug['P']) + print("\tBOTCH: where is it used?; FAILURE\n"); + return 0; + } break; } /* check whether shift result is used subsequently */ @@ -660,8 +691,11 @@ shiftprop(Flow *r) if(p1->to.reg != n) for (;;) { r1 = uniqs(r1); - if(r1 == nil) - FAIL("inconclusive"); + if(r1 == nil) { + if(debug['P']) + print("\tinconclusive; FAILURE\n"); + return 0; + } p1 = r1->prog; if(debug['P']) print("\n%P", p1); @@ -671,7 +705,9 @@ shiftprop(Flow *r) case 3: /* set, not used */ break; default:/* used */ - FAIL("reused"); + if(debug['P']) + print("\treused; FAILURE\n"); + return 0; } break; } @@ -941,7 +977,7 @@ xtramodes(Graph *g, Flow *r, Adr *a) * 4 if set and used * 0 otherwise (not touched) */ -int +static int copyu(Prog *p, Adr *v, Adr *s) { switch(p->as) { @@ -1572,3 +1608,12 @@ smallindir(Addr *a, Addr *reg) a->reg == reg->reg && 0 <= a->offset && a->offset < 4096; } + +void +excise(Flow *r) +{ + Prog *p; + + p = r->prog; + nopout(p); +} diff --git a/src/cmd/5g/prog.c b/src/cmd/5g/prog.c index a77f2336e9..9d5adefe69 100644 --- a/src/cmd/5g/prog.c +++ b/src/cmd/5g/prog.c @@ -5,7 +5,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" enum { @@ -148,4 +148,13 @@ proginfo(ProgInfo *info, Prog *p) if(((p->scond & C_SCOND) != C_SCOND_NONE) && (info->flags & RightWrite)) info->flags |= RightRead; + + switch(p->as) { + case ADIV: + case ADIVU: + case AMOD: + case AMODU: + info->regset |= RtoB(REG_R12); + break; + } } diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 93090ebe42..1216e01bd5 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -32,66 +32,11 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" -#define NREGVAR 32 -#define REGBITS ((uint64)0xffffffffull) -/*c2go enum { +enum { NREGVAR = 32, - REGBITS = 0xffffffff, }; -*/ - - void addsplits(void); -static Reg* firstr; -static int first = 1; - -int -rcmp(const void *a1, const void *a2) -{ - Rgn *p1, *p2; - int c1, c2; - - p1 = (Rgn*)a1; - p2 = (Rgn*)a2; - c1 = p2->cost; - c2 = p1->cost; - if(c1 -= c2) - return c1; - return p2->varno - p1->varno; -} - -void -excise(Flow *r) -{ - Prog *p; - - p = r->prog; - nopout(p); -} - -static void -setaddrs(Bits bit) -{ - int i, n; - Var *v; - Node *node; - - while(bany(&bit)) { - // convert each bit to a variable - i = bnum(bit); - node = var[i].node; - n = var[i].name; - biclr(&bit, i); - - // disable all pieces of that variable - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node && v->name == n) - v->addr = 2; - } - } -} static char* regname[] = { ".R0", @@ -128,1188 +73,26 @@ static char* regname[] = { ".F15", }; -static Node* regnodes[NREGVAR]; - -static void walkvardef(Node *n, Reg *r, int active); - -void -regopt(Prog *firstp) -{ - Reg *r, *r1; - Prog *p; - Graph *g; - int i, z, active; - uint32 vreg; - Bits bit; - ProgInfo info; - - if(first) { - fmtinstall('Q', Qconv); - first = 0; - } - - mergetemp(firstp); - - /* - * control flow is more complicated in generated go code - * than in generated c code. define pseudo-variables for - * registers, so we have complete register usage information. - */ - nvar = NREGVAR; - memset(var, 0, NREGVAR*sizeof var[0]); - 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++) { - externs.b[z] = 0; - params.b[z] = 0; - consts.b[z] = 0; - addrs.b[z] = 0; - ivar.b[z] = 0; - ovar.b[z] = 0; - } - - /* - * pass 1 - * build aux data structure - * allocate pcs - * find use and set of variables - */ - g = flowstart(firstp, sizeof(Reg)); - if(g == nil) { - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - return; - } - - firstr = (Reg*)g->start; - - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF || p->as == AVARKILL) - continue; - proginfo(&info, p); - - // Avoid making variables for direct-called functions. - if(p->as == ABL && p->to.name == NAME_EXTERN) - continue; - - bit = mkvar(r, &p->from); - if(info.flags & LeftRead) - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - if(info.flags & LeftAddr) - setaddrs(bit); - - if(info.flags & RegRead) - r->use1.b[0] |= RtoB(p->reg); - - if(info.flags & (RightAddr | RightRead | RightWrite)) { - bit = mkvar(r, &p->to); - if(info.flags & RightAddr) - setaddrs(bit); - if(info.flags & RightRead) - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - if(info.flags & RightWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - - /* the mod/div runtime routines smash R12 */ - if(p->as == ADIV || p->as == ADIVU || p->as == AMOD || p->as == AMODU) - r->set.b[0] |= RtoB(REG_R12); - } - if(firstr == R) - return; - - for(i=0; i<nvar; i++) { - Var *v = var+i; - if(v->addr) { - bit = blsh(i); - for(z=0; z<BITS; z++) - addrs.b[z] |= bit.b[z]; - } - - 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->f, 1); - - /* - * pass 2 - * find looping structure - */ - flowrpo(g); - - if(debug['R'] && debug['v']) - dumpit("pass2", &firstr->f, 1); - - /* - * pass 2.5 - * iterate propagating fat vardef covering forward - * r->act records vars with a VARDEF since the last CALL. - * (r->act will be reused in pass 5 for something else, - * but we'll be done with it by then.) - */ - active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->f.active = 0; - r->act = zbits; - } - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF && isfat(((Node*)(p->to.node))->type) && ((Node*)(p->to.node))->opt != nil) { - active++; - walkvardef(p->to.node, r, active); - } - } - - /* - * pass 3 - * iterate propagating usage - * back until flow graph is complete - */ -loop1: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - if(r->f.prog->as == ARET) - prop(r, zbits, zbits); -loop11: - /* pick up unreachable code */ - i = 0; - for(r = firstr; r != R; r = r1) { - r1 = (Reg*)r->f.link; - if(r1 && r1->f.active && !r->f.active) { - prop(r, zbits, zbits); - i = 1; - } - } - if(i) - goto loop11; - if(change) - goto loop1; - - if(debug['R'] && debug['v']) - dumpit("pass3", &firstr->f, 1); - - - /* - * pass 4 - * iterate propagating register/variable synchrony - * forward until graph is complete - */ -loop2: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - synch(firstr, zbits); - if(change) - goto loop2; - - addsplits(); - - if(debug['R'] && debug['v']) - dumpit("pass4", &firstr->f, 1); - - if(debug['R'] > 1) { - print("\nprop structure:\n"); - for(r = firstr; r != R; r = (Reg*)r->f.link) { - print("%d:%P", r->f.loop, r->f.prog); - for(z=0; z<BITS; z++) { - bit.b[z] = r->set.b[z] | - r->refahead.b[z] | r->calahead.b[z] | - r->refbehind.b[z] | r->calbehind.b[z] | - r->use1.b[z] | r->use2.b[z]; - bit.b[z] &= ~addrs.b[z]; - } - - if(bany(&bit)) { - print("\t"); - if(bany(&r->use1)) - print(" u1=%Q", r->use1); - if(bany(&r->use2)) - print(" u2=%Q", r->use2); - if(bany(&r->set)) - print(" st=%Q", r->set); - if(bany(&r->refahead)) - print(" ra=%Q", r->refahead); - if(bany(&r->calahead)) - print(" ca=%Q", r->calahead); - if(bany(&r->refbehind)) - print(" rb=%Q", r->refbehind); - if(bany(&r->calbehind)) - print(" cb=%Q", r->calbehind); - } - print("\n"); - } - } - - /* - * pass 4.5 - * move register pseudo-variables into regu. - */ - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; - - r->set.b[0] &= ~REGBITS; - r->use1.b[0] &= ~REGBITS; - r->use2.b[0] &= ~REGBITS; - r->refbehind.b[0] &= ~REGBITS; - r->refahead.b[0] &= ~REGBITS; - r->calbehind.b[0] &= ~REGBITS; - r->calahead.b[0] &= ~REGBITS; - r->regdiff.b[0] &= ~REGBITS; - r->act.b[0] &= ~REGBITS; - } - - if(debug['R'] && debug['v']) - dumpit("pass4.5", &firstr->f, 1); - - /* - * pass 5 - * isolate regions - * calculate costs (paint1) - */ - r = firstr; - if(r) { - for(z=0; z<BITS; z++) - bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & - ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->f.refset) { - // should never happen - all variables are preset - if(debug['w']) - print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - } - } - - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->act = zbits; - rgp = region; - nregion = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - for(z=0; z<BITS; z++) - bit.b[z] = r->set.b[z] & - ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->f.refset) { - if(debug['w']) - print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - excise(&r->f); - } - for(z=0; z<BITS; z++) - bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); - while(bany(&bit)) { - i = bnum(bit); - rgp->enter = r; - rgp->varno = i; - change = 0; - if(debug['R'] > 1) - print("\n"); - paint1(r, i); - biclr(&bit, i); - if(change <= 0) { - if(debug['R']) - print("%L $%d: %Q\n", - r->f.prog->lineno, change, blsh(i)); - continue; - } - rgp->cost = change; - nregion++; - if(nregion >= NRGN) { - if(debug['R'] > 1) - print("too many regions\n"); - goto brk; - } - rgp++; - } - } -brk: - qsort(region, nregion, sizeof(region[0]), rcmp); - - if(debug['R'] && debug['v']) - dumpit("pass5", &firstr->f, 1); - - /* - * pass 6 - * determine used registers (paint2) - * replace code (paint3) - */ - rgp = region; - if(debug['R'] && debug['v']) - print("\nregisterizing\n"); - for(i=0; i<nregion; i++) { - if(debug['R'] && debug['v']) - print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc); - bit = blsh(rgp->varno); - vreg = paint2(rgp->enter, rgp->varno, 0); - vreg = allreg(vreg, rgp); - if(debug['R']) { - print("%L $%d %R: %Q\n", - rgp->enter->f.prog->lineno, - rgp->cost, - rgp->regno, - bit); - } - if(rgp->regno != 0) - paint3(rgp->enter, rgp->varno, vreg, rgp->regno); - rgp++; - } - - /* - * free aux structures. peep allocates new ones. - */ - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - flowend(g); - firstr = R; - - if(debug['R'] && debug['v']) { - // Rebuild flow graph, since we inserted instructions - g = flowstart(firstp, sizeof(Reg)); - firstr = (Reg*)g->start; - dumpit("pass6", &firstr->f, 1); - flowend(g); - firstr = R; - } - - /* - * pass 7 - * peep-hole on basic block - */ - if(!debug['R'] || debug['P']) { - peep(firstp); - } - - if(debug['R'] && debug['v']) - dumpit("pass7", &firstr->f, 1); - - /* - * last pass - * eliminate nops - * free aux structures - * adjust the stack pointer - * MOVW.W R1,-12(R13) <<- start - * MOVW R0,R1 - * MOVW R1,8(R13) - * MOVW $0,R1 - * MOVW R1,4(R13) - * BL ,runtime.newproc+0(SB) - * MOVW &ft+-32(SP),R7 <<- adjust - * MOVW &j+-40(SP),R6 <<- adjust - * MOVW autotmp_0003+-24(SP),R5 <<- adjust - * MOVW $12(R13),R13 <<- finish - */ - vreg = 0; - for(p = firstp; p != P; p = p->link) { - while(p->link != P && p->link->as == ANOP) - p->link = p->link->link; - if(p->to.type == TYPE_BRANCH) - 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 -// print("%P adjusting %d\n", p, vreg); - continue; - } - if(p->from.type == TYPE_CONST && p->to.type == TYPE_REG) { - if(p->from.offset != vreg) - print("in and out different\n"); -// print("%P finish %d\n", p, vreg); - vreg = 0; // done adjust region - continue; - } - -// print("%P %d %d from type\n", p, p->from.type, TYPE_CONST); -// print("%P %d %d to type\n\n", p, p->to.type, TYPE_REG); - } - - if(p->as == AMOVW && vreg != 0) { - if(p->from.sym != nil) - if(p->from.name == NAME_AUTO || p->from.name == NAME_PARAM) { - p->from.offset += vreg; -// print("%P adjusting from %d %d\n", p, vreg, p->from.type); - } - if(p->to.sym != nil) - if(p->to.name == NAME_AUTO || p->to.name == NAME_PARAM) { - p->to.offset += vreg; -// print("%P adjusting to %d %d\n", p, vreg, p->from.type); - } - } - } -} - -static void -walkvardef(Node *n, Reg *r, int active) -{ - Reg *r1, *r2; - int bn; - Var *v; - - for(r1=r; r1!=R; r1=(Reg*)r1->f.s1) { - if(r1->f.active == active) - break; - r1->f.active = active; - if(r1->f.prog->as == AVARKILL && r1->f.prog->to.node == n) - break; - for(v=n->opt; v!=nil; v=v->nextinnode) { - bn = v - var; - biset(&r1->act, bn); - } - if(r1->f.prog->as == ABL) - break; - } - - for(r2=r; r2!=r1; r2=(Reg*)r2->f.s1) - if(r2->f.s2 != nil) - walkvardef(n, (Reg*)r2->f.s2, active); -} - -void -addsplits(void) -{ - Reg *r, *r1; - int z, i; - Bits bit; - - for(r = firstr; r != R; r = (Reg*)r->f.link) { - if(r->f.loop > 1) - continue; - if(r->f.prog->as == ABL) - continue; - if(r->f.prog->as == ADUFFZERO) - continue; - if(r->f.prog->as == ADUFFCOPY) - continue; - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) { - if(r1->f.loop <= 1) - continue; - for(z=0; z<BITS; z++) - bit.b[z] = r1->calbehind.b[z] & - (r->refahead.b[z] | r->use1.b[z] | r->use2.b[z]) & - ~(r->calahead.b[z] & addrs.b[z]); - while(bany(&bit)) { - i = bnum(bit); - biclr(&bit, i); - } - } - } -} - -/* - * add mov b,rn - * just after r - */ -void -addmove(Reg *r, int bn, int rn, int f) -{ - Prog *p, *p1, *p2; - Adr *a; - Var *v; - - p1 = mal(sizeof(*p1)); - *p1 = zprog; - p = r->f.prog; - - // If there's a stack fixup coming (after BL newproc or BL deferproc), - // delay the load until after the fixup. - p2 = p->link; - if(p2 && p2->as == AMOVW && p2->from.type == TYPE_ADDR && p2->from.reg == REGSP && p2->to.reg == REGSP && p2->to.type == TYPE_REG) - p = p2; - - p1->link = p->link; - p->link = p1; - p1->lineno = p->lineno; - - v = var + bn; - - a = &p1->to; - a->name = v->name; - a->node = v->node; - a->sym = linksym(v->node->sym); - a->offset = v->offset; - a->etype = v->etype; - a->type = TYPE_MEM; - if(a->etype == TARRAY) - a->type = TYPE_ADDR; - else if(a->sym == nil) - a->type = TYPE_CONST; - - if(v->addr) - fatal("addmove: shouldn't be doing this %A\n", a); - - switch(v->etype) { - default: - print("What is this %E\n", v->etype); - - case TINT8: - p1->as = AMOVBS; - break; - case TBOOL: - case TUINT8: -//print("movbu %E %d %S\n", v->etype, bn, v->sym); - p1->as = AMOVBU; - break; - case TINT16: - p1->as = AMOVHS; - break; - case TUINT16: - p1->as = AMOVHU; - break; - case TINT32: - case TUINT32: - case TPTR32: - p1->as = AMOVW; - break; - case TFLOAT32: - p1->as = AMOVF; - break; - case TFLOAT64: - p1->as = AMOVD; - break; - } - - p1->from.type = TYPE_REG; - p1->from.reg = rn; - if(!f) { - p1->from = *a; - *a = zprog.from; - a->type = TYPE_REG; - a->reg = rn; - if(v->etype == TUINT8 || v->etype == TBOOL) - p1->as = AMOVBU; - if(v->etype == TUINT16) - p1->as = AMOVHU; - } - if(debug['R']) - print("%P\t.a%P\n", p, p1); -} - -static int -overlap(int32 o1, int w1, int32 o2, int w2) -{ - int32 t1, t2; - - t1 = o1+w1; - t2 = o2+w2; - - if(!(t1 > o2 && t2 > o1)) - return 0; - - return 1; -} - -Bits -mkvar(Reg *r, Adr *a) -{ - Var *v; - int i, t, n, et, z, w, flag; - int32 o; - Bits bit; - Node *node; - - // mark registers used - t = a->type; - - flag = 0; - switch(t) { - default: - print("type %d %d %D\n", t, a->name, a); - goto none; - - case TYPE_NONE: - case TYPE_FCONST: - case TYPE_BRANCH: - break; - - - case TYPE_REGREG: - case TYPE_REGREG2: - bit = zbits; - if(a->offset != 0) - bit.b[0] |= RtoB(a->offset); - if(a->reg != 0) - bit.b[0] |= RtoB(a->reg); - return bit; - - case TYPE_CONST: - if(a->reg != 0) - fatal("found CONST instead of ADDR: %D", a); - break; - - case TYPE_ADDR: - case TYPE_REG: - case TYPE_SHIFT: - if(a->reg != 0) { - bit = zbits; - bit.b[0] = RtoB(a->reg); - return bit; - } - break; - - case TYPE_MEM: - if(a->reg != 0) { - if(a == &r->f.prog->from) - r->use1.b[0] |= RtoB(a->reg); - else - r->use2.b[0] |= RtoB(a->reg); - if(r->f.prog->scond & (C_PBIT|C_WBIT)) - r->set.b[0] |= RtoB(a->reg); - } - break; - } - - switch(a->name) { - default: - goto none; - - case NAME_EXTERN: - case NAME_STATIC: - case NAME_AUTO: - case NAME_PARAM: - n = a->name; - break; - } - - node = a->node; - if(node == N || node->op != ONAME || node->orig == N) - goto none; - node = node->orig; - if(node->orig != node) - fatal("%D: bad node", a); - if(node->sym == S || node->sym->name[0] == '.') - goto none; - 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; - if(v->node == node && v->name == n) { - if(v->offset == o) - if(v->etype == et) - if(v->width == w) - if(!flag) - return blsh(i); - - // if they overlap, disable both - if(overlap(v->offset, v->width, o, w)) { - v->addr = 1; - flag = 1; - } - } - } - - switch(et) { - case 0: - case TFUNC: - goto none; - } - - if(nvar >= NVAR) { - if(debug['w'] > 1 && node) - fatal("variable not optimized: %D", a); - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node) - v->addr = 1; - } - goto none; - } - - i = nvar; - nvar++; -//print("var %d %E %D %S\n", i, et, a, s); - v = var+i; - v->offset = o; - v->name = n; - v->etype = et; - v->width = w; - v->addr = flag; // funny punning - v->node = node; - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v->nextinnode = node->opt; - node->opt = v; - - bit = blsh(i); - if(n == NAME_EXTERN || n == NAME_STATIC) - for(z=0; z<BITS; z++) - externs.b[z] |= bit.b[z]; - if(n == NAME_PARAM) - for(z=0; z<BITS; z++) - params.b[z] |= bit.b[z]; - - if(node->class == PPARAM) - for(z=0; z<BITS; z++) - ivar.b[z] |= bit.b[z]; - if(node->class == PPARAMOUT) - for(z=0; z<BITS; z++) - ovar.b[z] |= bit.b[z]; - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in ../gc/plive.c does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.c:/^typecheckclosure and dcl.c:/^oldname. - if(node->addrtaken) - v->addr = 1; - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if(node->class == PEXTERN || (hasdefer && node->class == PPARAMOUT)) - v->addr = 1; - - if(debug['R']) - print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); - - return bit; - -none: - return zbits; -} - -void -prop(Reg *r, Bits ref, Bits cal) +char** +regnames(int *n) { - Reg *r1, *r2; - int z, i, j; - Var *v, *v1; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { - for(z=0; z<BITS; z++) { - ref.b[z] |= r1->refahead.b[z]; - if(ref.b[z] != r1->refahead.b[z]) { - r1->refahead.b[z] = ref.b[z]; - change++; - } - cal.b[z] |= r1->calahead.b[z]; - if(cal.b[z] != r1->calahead.b[z]) { - r1->calahead.b[z] = cal.b[z]; - change++; - } - } - switch(r1->f.prog->as) { - case ABL: - if(noreturn(r1->f.prog)) - break; - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for(z=0; z<BITS; z++) { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1->act.b[z]; - ref.b[z] = 0; - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for(z=0; z<BITS; z++) { - if(cal.b[z] == 0) - continue; - for(i=0; i<64; i++) { - if(z*64+i >= nvar || ((cal.b[z]>>i)&1) == 0) - continue; - v = var+z*64+i; - if(v->node->opt == nil) // v represents fixed register, not Go variable - continue; - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1 = v->node->opt; - j = v1 - var; - if(v == v1 || !btest(&cal, j)) { - for(; v1 != nil; v1 = v1->nextinnode) { - j = v1 - var; - biset(&cal, j); - } - } - } - } - break; - - case ATEXT: - for(z=0; z<BITS; z++) { - cal.b[z] = 0; - ref.b[z] = 0; - } - break; - - case ARET: - for(z=0; z<BITS; z++) { - cal.b[z] = externs.b[z] | ovar.b[z]; - ref.b[z] = 0; - } - break; - } - for(z=0; z<BITS; z++) { - ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | - r1->use1.b[z] | r1->use2.b[z]; - cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); - r1->refbehind.b[z] = ref.b[z]; - r1->calbehind.b[z] = cal.b[z]; - } - if(r1->f.active) - break; - r1->f.active = 1; - } - for(; r != r1; r = (Reg*)r->f.p1) - for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) - prop(r2, r->refbehind, r->calbehind); + *n = NREGVAR; + return regname; } -void -synch(Reg *r, Bits dif) +uint64 +excludedregs(void) { - Reg *r1; - int z; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { - for(z=0; z<BITS; z++) { - dif.b[z] = (dif.b[z] & - ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | - r1->set.b[z] | r1->regdiff.b[z]; - if(dif.b[z] != r1->regdiff.b[z]) { - r1->regdiff.b[z] = dif.b[z]; - change++; - } - } - if(r1->f.active) - break; - r1->f.active = 1; - for(z=0; z<BITS; z++) - dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->f.s2 != nil) - synch((Reg*)r1->f.s2, dif); - } + return RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); } -uint32 -allreg(uint32 b, Rgn *r) +uint64 +doregbits(int r) { - Var *v; - int i; - - v = var + r->varno; - r->regno = 0; - switch(v->etype) { - - default: - fatal("unknown etype %d/%E", bitno(b), v->etype); - break; - - case TINT8: - case TUINT8: - case TINT16: - case TUINT16: - case TINT32: - case TUINT32: - case TINT: - case TUINT: - case TUINTPTR: - case TBOOL: - case TPTR32: - i = BtoR(~b); - if(i && r->cost >= 0) { - r->regno = i; - return RtoB(i); - } - break; - - case TFLOAT32: - case TFLOAT64: - i = BtoF(~b); - if(i && r->cost >= 0) { - r->regno = i; - return RtoB(i); - } - break; - - case TINT64: - case TUINT64: - case TPTR64: - case TINTER: - case TSTRUCT: - case TARRAY: - break; - } + USED(r); return 0; } -void -paint1(Reg *r, int bn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL<<(bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->f.loop; - if(debug['R'] > 1) - print("%d%P\td %Q $%d\n", r->f.loop, - r->f.prog, blsh(bn), change); - } - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - - if(r->f.prog->as != ANOP) { // don't give credit for NOPs - if(r->use1.b[z] & bb) { - change += CREF * r->f.loop; - if(debug['R'] > 1) - print("%d%P\tu1 %Q $%d\n", r->f.loop, - p, blsh(bn), change); - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - change += CREF * r->f.loop; - if(debug['R'] > 1) - print("%d%P\tu2 %Q $%d\n", r->f.loop, - p, blsh(bn), change); - } - } - - if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->f.loop; - if(debug['R'] > 1) - print("%d%P\tst %Q $%d\n", r->f.loop, - p, blsh(bn), change); - } - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint1(r1, bn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint1(r1, bn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -uint32 -paint2(Reg *r, int bn, int depth) -{ - Reg *r1; - int z; - uint64 bb, vreg; - - z = bn/64; - bb = 1LL << (bn%64); - vreg = regbits; - if(!(r->act.b[z] & bb)) - return vreg; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(!(r1->act.b[z] & bb)) - break; - r = r1; - } - for(;;) { - if(debug['R'] && debug['v']) - print(" paint2 %d %P\n", depth, r->f.prog); - - r->act.b[z] &= ~bb; - - vreg |= r->regu; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(!(r->act.b[z] & bb)) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } - return vreg; -} - -void -paint3(Reg *r, int bn, uint32 rb, int rn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL << (bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) - addmove(r, bn, rn, 0); - - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - if(r->use1.b[z] & bb) { - if(debug['R']) - print("%P", p); - addreg(&p->from, rn); - if(debug['R']) - print("\t.c%P\n", p); - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - if(debug['R']) - print("%P", p); - addreg(&p->to, rn); - if(debug['R']) - print("\t.c%P\n", p); - } - - if(STORE(r) & r->regdiff.b[z] & bb) - addmove(r, bn, rn, 1); - r->regu |= rb; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint3(r1, bn, rb, rn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint3(r1, bn, rb, rn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -void -addreg(Adr *a, int rn) -{ - a->sym = nil; - a->node = nil; - a->name = NAME_NONE; - a->type = TYPE_REG; - a->reg = rn; -} - /* * bit reg * 0 R0 @@ -1324,26 +107,26 @@ addreg(Adr *a, int rn) * ... ... * 31 F15 */ -uint32 +uint64 RtoB(int r) { if(REG_R0 <= r && r <= REG_R15) { if(r >= REGTMP-2 && r != REG_R12) // excluded R9 and R10 for m and g, but not R12 return 0; - return 1L << (r - REG_R0); + return 1ULL << (r - REG_R0); } if(REG_F0 <= r && r <= REG_F15) { if(r < REG_F2 || r > REG_F0+NFREG-1) return 0; - return 1L << ((r - REG_F0) + 16); + return 1ULL << ((r - REG_F0) + 16); } return 0; } int -BtoR(uint32 b) +BtoR(uint64 b) { // TODO Allow R0 and R1, but be careful with a 0 return // TODO Allow R9. Only R10 is reserved now (just g, not m). @@ -1354,88 +137,10 @@ BtoR(uint32 b) } int -BtoF(uint32 b) +BtoF(uint64 b) { b &= 0xfffc0000L; if(b == 0) return 0; return bitno(b) - 16 + REG_F0; } - -void -dumpone(Flow *f, int isreg) -{ - int z; - Bits bit; - Reg *r; - - print("%d:%P", f->loop, f->prog); - if(isreg) { - r = (Reg*)f; - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); - } - } - print("\n"); -} - -void -dumpit(char *str, Flow *r0, int isreg) -{ - Flow *r, *r1; - - print("\n%s\n", str); - for(r = r0; r != nil; r = r->link) { - dumpone(r, isreg); - r1 = r->p2; - if(r1 != nil) { - print(" pred:"); - for(; r1 != nil; r1 = r1->p2link) - print(" %.4ud", (int)r1->prog->pc); - if(r->p1 != nil) - print(" (and %.4ud)", (int)r->p1->prog->pc); - else - print(" (only)"); - print("\n"); - } - // Print successors if it's not just the next one - if(r->s1 != r->link || r->s2 != nil) { - print(" succ:"); - if(r->s1 != nil) - print(" %.4ud", (int)r->s1->prog->pc); - if(r->s2 != nil) - print(" %.4ud", (int)r->s2->prog->pc); - print("\n"); - } - } -} diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index fa1dcc34e8..a266a6b87c 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -91,6 +91,7 @@ enum REGG = REGEXT-0, REGM = REGEXT-1, + REGCTXT = REG_R7, REGTMP = REG_R11, REGSP = REG_R13, REGLINK = REG_R14, diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 5993079126..ed4d68a1fb 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -33,16 +33,9 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/macho.h" #include "../ld/dwarf.h" - -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"; -char dragonflydynld[] = "XXX"; -char solarisdynld[] = "XXX"; - static int needlib(char *name) { @@ -63,8 +56,6 @@ needlib(char *name) return 0; } -int nelfsym = 1; - static void addpltsym(Link*, LSym*); static void addgotsym(Link*, LSym*); static void addgotsyminternal(Link*, LSym*); @@ -127,11 +118,11 @@ adddynrel(LSym *s, Reloc *r) addgotsym(ctxt, targ); } r->type = R_CONST; // write r->add during relocsym - r->sym = S; + r->sym = nil; r->add += targ->got; return; - case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P + case 256 + R_ARM_GOT_PREL: // GOT(nil) + A - nil if(targ->type != SDYNIMPORT) { addgotsyminternal(ctxt, targ); } else { @@ -178,7 +169,7 @@ adddynrel(LSym *s, Reloc *r) // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it r->sym->type = 0; } - r->sym = S; + r->sym = nil; return; case 256 + R_ARM_PC24: @@ -210,9 +201,9 @@ adddynrel(LSym *s, Reloc *r) adddynsym(ctxt, targ); rel = linklookup(ctxt, ".rel", 0); addaddrplus(ctxt, rel, s, r->off); - adduint32(ctxt, rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a S + A dynmic reloc + adduint32(ctxt, rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a nil + A dynmic reloc r->type = R_CONST; // write r->add during relocsym - r->sym = S; + r->sym = nil; return; } break; @@ -227,7 +218,7 @@ elfreloc1(Reloc *r, vlong sectoff) { int32 elfsym; - LPUT(sectoff); + thearch.lput(sectoff); elfsym = r->xsym->elfsym; switch(r->type) { @@ -236,14 +227,14 @@ elfreloc1(Reloc *r, vlong sectoff) case R_ADDR: if(r->siz == 4) - LPUT(R_ARM_ABS32 | elfsym<<8); + thearch.lput(R_ARM_ABS32 | elfsym<<8); else return -1; break; case R_PCREL: if(r->siz == 4) - LPUT(R_ARM_REL32 | elfsym<<8); + thearch.lput(R_ARM_REL32 | elfsym<<8); else return -1; break; @@ -251,9 +242,9 @@ elfreloc1(Reloc *r, vlong sectoff) case R_CALLARM: if(r->siz == 4) { if((r->add & 0xff000000) == 0xeb000000) // BL - LPUT(R_ARM_CALL | elfsym<<8); + thearch.lput(R_ARM_CALL | elfsym<<8); else - LPUT(R_ARM_JUMP24 | elfsym<<8); + thearch.lput(R_ARM_JUMP24 | elfsym<<8); } else return -1; break; @@ -261,9 +252,9 @@ elfreloc1(Reloc *r, vlong sectoff) case R_TLS: if(r->siz == 4) { if(flag_shared) - LPUT(R_ARM_TLS_IE32 | elfsym<<8); + thearch.lput(R_ARM_TLS_IE32 | elfsym<<8); else - LPUT(R_ARM_TLS_LE32 | elfsym<<8); + thearch.lput(R_ARM_TLS_LE32 | elfsym<<8); } else return -1; break; @@ -301,10 +292,58 @@ elfsetupplt(void) int machoreloc1(Reloc *r, vlong sectoff) { - USED(r); - USED(sectoff); + uint32 v; + LSym *rs; - return -1; + rs = r->xsym; + + if(rs->type == SHOSTOBJ || r->type == R_CALLARM) { + if(rs->dynid < 0) { + diag("reloc %d to non-macho symbol %s type=%d", r->type, rs->name, rs->type); + return -1; + } + v = rs->dynid; + v |= 1<<27; // external relocation + } else { + v = rs->sect->extnum; + if(v == 0) { + diag("reloc %d to symbol %s in non-macho section %s type=%d", r->type, rs->name, rs->sect->name, rs->type); + return -1; + } + } + + switch(r->type) { + default: + return -1; + case R_ADDR: + v |= MACHO_GENERIC_RELOC_VANILLA<<28; + break; + case R_CALLARM: + v |= 1<<24; // pc-relative bit + v |= MACHO_ARM_RELOC_BR24<<28; + break; + } + + switch(r->siz) { + default: + return -1; + case 1: + v |= 0<<25; + break; + case 2: + v |= 1<<25; + break; + case 4: + v |= 2<<25; + break; + case 8: + v |= 3<<25; + break; + } + + thearch.lput(sectoff); + thearch.lput(v); + return 0; } @@ -333,6 +372,14 @@ archreloc(Reloc *r, LSym *s, vlong *val) diag("missing section for %s", rs->name); r->xsym = rs; + // ld64 for arm seems to want the symbol table to contain offset + // into the section rather than pseudo virtual address that contains + // the section load address. + // we need to compensate that by removing the instruction's address + // from addend. + if(HEADTYPE == Hdarwin) + r->xadd -= symaddr(s) + r->off; + *val = braddoff((0xff000000U & (uint32)r->add), (0xffffff & (uint32)(r->xadd / 4))); return 0; @@ -539,6 +586,8 @@ adddynlib(char *lib) if(s->size == 0) addstring(s, ""); elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else if(HEADTYPE == Hdarwin) { + machoadddynlib(lib); } else { diag("adddynlib: unsupported binary format"); } @@ -547,7 +596,7 @@ adddynlib(char *lib) void asmb(void) { - uint32 symo; + uint32 symo, dwarfoff, machlink; Section *sect; LSym *sym; int i; @@ -583,6 +632,22 @@ asmb(void) cseek(segdata.fileoff); datblk(segdata.vaddr, segdata.filelen); + machlink = 0; + if(HEADTYPE == Hdarwin) { + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + + if(!debug['w']) { // TODO(minux): enable DWARF Support + dwarfoff = rnd(HEADR+segtext.len, INITRND) + rnd(segdata.filelen, INITRND); + cseek(dwarfoff); + + segdwarf.fileoff = cpos(); + dwarfemitdebugsections(); + segdwarf.filelen = cpos() - segdwarf.fileoff; + } + machlink = domacholink(); + } + /* output symbol table */ symsize = 0; lcsize = 0; @@ -599,6 +664,9 @@ asmb(void) case Hplan9: symo = segdata.fileoff+segdata.filelen; break; + case Hdarwin: + symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; + break; ElfSym: symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, INITRND); @@ -635,6 +703,10 @@ asmb(void) cflush(); } break; + case Hdarwin: + if(linkmode == LinkExternal) + machoemitreloc(); + break; } } @@ -646,14 +718,14 @@ asmb(void) switch(HEADTYPE) { default: case Hplan9: /* plan 9 */ - LPUT(0x647); /* magic */ - LPUT(segtext.filelen); /* sizes */ - LPUT(segdata.filelen); - LPUT(segdata.len - segdata.filelen); - LPUT(symsize); /* nsyms */ - LPUT(entryvalue()); /* va of entry */ - LPUT(0L); - LPUT(lcsize); + thearch.lput(0x647); /* magic */ + thearch.lput(segtext.filelen); /* sizes */ + thearch.lput(segdata.filelen); + thearch.lput(segdata.len - segdata.filelen); + thearch.lput(symsize); /* nsyms */ + thearch.lput(entryvalue()); /* va of entry */ + thearch.lput(0L); + thearch.lput(lcsize); break; case Hlinux: case Hfreebsd: @@ -662,6 +734,9 @@ asmb(void) case Hnacl: asmbelf(symo); break; + case Hdarwin: + asmbmacho(); + break; } cflush(); if(debug['c']){ @@ -673,18 +748,3 @@ asmb(void) print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); } } - -int32 -rnd(int32 v, int32 r) -{ - int32 c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index f9cdc5bd7e..b32b89bb06 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -32,7 +32,6 @@ #include <libc.h> #include <bio.h> #include <link.h> -#include "5.out.h" enum { @@ -41,43 +40,14 @@ enum IntSize = 4, RegSize = 4, MaxAlign = 8, // max data alignment - FuncAlign = 4 // single-instruction alignment + FuncAlign = 4, // single-instruction alignment + MINLC = 4, }; #ifndef EXTERN #define EXTERN extern #endif -#define P ((Prog*)0) -#define S ((LSym*)0) - -enum -{ -/* mark flags */ - FOLL = 1<<0, - LABEL = 1<<1, - LEAF = 1<<2, - - MINLC = 4, -}; - -EXTERN int32 autosize; -EXTERN LSym* datap; -EXTERN int debug[128]; -EXTERN char* noname; -EXTERN Prog* lastp; -EXTERN int32 lcsize; -EXTERN char literal[32]; -EXTERN int nerrors; -EXTERN int32 instoffset; -EXTERN char* rpath; -EXTERN uint32 stroffset; -EXTERN int32 symsize; -EXTERN int armsize; - -#pragma varargck type "I" uint32* - -int Iconv(Fmt *fp); void adddynlib(char *lib); void adddynrel(LSym *s, Reloc *r); void adddynrela(LSym *rel, LSym *s, Reloc *r); @@ -89,13 +59,6 @@ int elfreloc1(Reloc *r, vlong sectoff); void elfsetupplt(void); void listinit(void); int machoreloc1(Reloc *r, vlong sectoff); -void main(int argc, char *argv[]); -int32 rnd(int32 v, int32 r); - -/* Native is little-endian */ -#define LPUT(a) lputl(a) -#define WPUT(a) wputl(a) -#define VPUT(a) abort() /* Used by ../ld/dwarf.c */ enum diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 875fc3e6b2..d4cc8acd7c 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -37,34 +37,4 @@ void listinit(void) { listinit5(); - fmtinstall('I', Iconv); -} - -int -Iconv(Fmt *fp) -{ - int i, n; - uint32 *p; - char *s; - Fmt fmt; - - n = fp->prec; - fp->prec = 0; - if(!(fp->flags&FmtPrec) || n < 0) - return fmtstrcpy(fp, "%I"); - fp->flags &= ~FmtPrec; - p = va_arg(fp->args, uint32*); - - // format into temporary buffer and - // call fmtstrcpy to handle padding. - fmtstrinit(&fmt); - for(i=0; i<n/4; i++) { - if(i > 0) - fmtprint(&fmt, " "); - fmtprint(&fmt, "%.8ux", *p++); - } - s = fmtstrflush(&fmt); - fmtstrcpy(fp, s); - free(s); - return 0; } diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index c6f60ee7c8..a7bd0f45ea 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -33,15 +33,55 @@ #include "l.h" #include "../ld/lib.h" #include "../ld/elf.h" +#include "../ld/macho.h" #include "../ld/dwarf.h" #include <ar.h> -char *thestring = "arm"; -LinkArch *thelinkarch = &linkarm; +void +main(int argc, char **argv) +{ + linkarchinit(); + ldmain(argc, argv); +} + void linkarchinit(void) { + thestring = "arm"; + thelinkarch = &linkarm; + + thearch.thechar = thechar; + thearch.ptrsize = thelinkarch->ptrsize; + thearch.intsize = thelinkarch->ptrsize; + thearch.regsize = thelinkarch->regsize; + thearch.funcalign = FuncAlign; + thearch.maxalign = MaxAlign; + thearch.minlc = MINLC; + thearch.dwarfregsp = DWARFREGSP; + + thearch.adddynlib = adddynlib; + thearch.adddynrel = adddynrel; + thearch.adddynsym = adddynsym; + thearch.archinit = archinit; + thearch.archreloc = archreloc; + thearch.archrelocvariant = archrelocvariant; + thearch.asmb = asmb; + thearch.elfreloc1 = elfreloc1; + thearch.elfsetupplt = elfsetupplt; + thearch.gentext = gentext; + thearch.listinit = listinit; + thearch.machoreloc1 = machoreloc1; + thearch.lput = lputl; + thearch.wput = wputl; + thearch.vput = vputl; + + thearch.linuxdynld = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI + thearch.freebsddynld = "/usr/libexec/ld-elf.so.1"; + thearch.openbsddynld = "XXX"; + thearch.netbsddynld = "/libexec/ld.elf_so"; + thearch.dragonflydynld = "XXX"; + thearch.solarisdynld = "XXX"; } void @@ -64,6 +104,7 @@ archinit(void) case Hlinux: case Hfreebsd: case Hnacl: + case Hdarwin: break; } @@ -104,6 +145,17 @@ archinit(void) if(INITRND == -1) INITRND = 0x10000; break; + case Hdarwin: /* apple MACH */ + debug['w'] = 1; // disable DWARF generataion + machoinit(); + HEADR = INITIAL_MACHO_HEADR; + if(INITTEXT == -1) + INITTEXT = 4096+HEADR; + if(INITDAT == -1) + INITDAT = 0; + if(INITRND == -1) + INITRND = 4096; + break; } if(INITDAT != 0 && INITRND != 0) print("warning: -D0x%ux is ignored because of -R0x%ux\n", diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index d1645cc568..77ab2d2167 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -790,7 +790,7 @@ agenr(Node *n, Node *a, Node *res) void agen(Node *n, Node *res) { - Node *nl, *nr; + Node *nl; Node n1, n2; if(debug['g']) { @@ -827,8 +827,6 @@ agen(Node *n, Node *res) } nl = n->left; - nr = n->right; - USED(nr); switch(n->op) { default: @@ -1065,17 +1063,7 @@ bgen(Node *n, int true, int likely, Prog *to) switch(n->op) { default: - def: - regalloc(&n1, n->type, N); - cgen(n, &n1); - nodconst(&n2, n->type, 0); - gins(optoas(OCMP, n->type), &n1, &n2); - a = AJNE; - if(!true) - a = AJEQ; - patch(gbranch(a, n->type, likely), to); - regfree(&n1); - goto ret; + goto def; case OLITERAL: // need to ask if it is bool? @@ -1095,27 +1083,20 @@ bgen(Node *n, int true, int likely, Prog *to) goto ret; case OANDAND: - if(!true) - goto caseor; - - caseand: - p1 = gbranch(AJMP, T, 0); - p2 = gbranch(AJMP, T, 0); - patch(p1, pc); - 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; - case OOROR: - if(!true) - goto caseand; - - caseor: - bgen(n->left, true, likely, to); - bgen(n->right, true, likely, to); + if((n->op == OANDAND) == true) { + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); + patch(p1, to); + patch(p2, pc); + } else { + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); + } goto ret; case OEQ: @@ -1273,6 +1254,18 @@ bgen(Node *n, int true, int likely, Prog *to) } goto ret; +def: + regalloc(&n1, n->type, N); + cgen(n, &n1); + nodconst(&n2, n->type, 0); + gins(optoas(OCMP, n->type), &n1, &n2); + a = AJNE; + if(!true) + a = AJEQ; + patch(gbranch(a, n->type, likely), to); + regfree(&n1); + goto ret; + ret: ; } @@ -1390,22 +1383,25 @@ sgen(Node *n, Node *ns, int64 w) return; } + nodreg(&noddi, types[tptr], REG_DI); + nodreg(&nodsi, types[tptr], REG_SI); + if(n->ullman >= ns->ullman) { - agenr(n, &nodr, N); + agenr(n, &nodr, &nodsi); if(ns->op == ONAME) gvardef(ns); - agenr(ns, &nodl, N); + agenr(ns, &nodl, &noddi); } else { if(ns->op == ONAME) gvardef(ns); - agenr(ns, &nodl, N); - agenr(n, &nodr, N); + agenr(ns, &nodl, &noddi); + agenr(n, &nodr, &nodsi); } - nodreg(&noddi, types[tptr], REG_DI); - nodreg(&nodsi, types[tptr], REG_SI); - gmove(&nodl, &noddi); - gmove(&nodr, &nodsi); + if(nodl.val.u.reg != REG_DI) + gmove(&nodl, &noddi); + if(nodr.val.u.reg != REG_SI) + gmove(&nodr, &nodsi); regfree(&nodl); regfree(&nodr); @@ -1454,6 +1450,18 @@ sgen(Node *n, Node *ns, int64 w) p->to.sym = linksym(pkglookup("duffcopy", runtimepkg)); // 14 and 128 = magic constants: see ../../runtime/asm_amd64.s p->to.offset = 14*(128-q); + } else if(!nacl && c == 0) { + // We don't need the MOVSQ side-effect of updating SI and DI, + // and issuing a sequence of MOVQs directly is faster. + nodsi.op = OINDREG; + noddi.op = OINDREG; + while(q > 0) { + gmove(&nodsi, &cx); // MOVQ x+(SI),CX + gmove(&cx, &noddi); // MOVQ CX,x+(DI) + nodsi.xoffset += 8; + noddi.xoffset += 8; + q--; + } } else while(q > 0) { gins(AMOVSQ, N, N); // MOVQ *(SI)+,*(DI)+ @@ -1468,24 +1476,29 @@ sgen(Node *n, Node *ns, int64 w) } else if(w < 8 || c <= 4) { nodsi.op = OINDREG; noddi.op = OINDREG; + cx.type = types[TINT32]; nodsi.type = types[TINT32]; noddi.type = types[TINT32]; if(c > 4) { nodsi.xoffset = 0; noddi.xoffset = 0; - gmove(&nodsi, &noddi); + gmove(&nodsi, &cx); + gmove(&cx, &noddi); } nodsi.xoffset = c-4; noddi.xoffset = c-4; - gmove(&nodsi, &noddi); + gmove(&nodsi, &cx); + gmove(&cx, &noddi); } else { nodsi.op = OINDREG; noddi.op = OINDREG; + cx.type = types[TINT64]; nodsi.type = types[TINT64]; noddi.type = types[TINT64]; nodsi.xoffset = c-8; noddi.xoffset = c-8; - gmove(&nodsi, &noddi); + gmove(&nodsi, &cx); + gmove(&cx, &noddi); } } @@ -1519,7 +1532,7 @@ cadable(Node *n) int componentgen(Node *nr, Node *nl) { - Node nodl, nodr; + Node nodl, nodr, tmp; Type *t; int freel, freer; vlong fldcount; @@ -1567,7 +1580,7 @@ componentgen(Node *nr, Node *nl) nodl = *nl; if(!cadable(nl)) { - if(nr == N || !cadable(nr)) + if(nr != N && !cadable(nr)) goto no; igen(nl, &nodl, N); freel = 1; @@ -1579,6 +1592,12 @@ componentgen(Node *nr, Node *nl) 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; } // nl and nr are 'cadable' which basically means they are names (variables) now. @@ -1615,8 +1634,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1625,8 +1643,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1635,8 +1652,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_cap-Array_nel; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1650,8 +1666,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1660,8 +1675,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1675,8 +1689,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1685,8 +1698,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; diff --git a/src/cmd/6g/galign.c b/src/cmd/6g/galign.c index 83833e189b..fa9998b0ee 100644 --- a/src/cmd/6g/galign.c +++ b/src/cmd/6g/galign.c @@ -15,9 +15,9 @@ linkarchinit(void) { if(strcmp(getgoarch(), "amd64p32") == 0) { thelinkarch = &linkamd64p32; - arch.thelinkarch = thelinkarch; + thearch.thelinkarch = thelinkarch; thestring = "amd64p32"; - arch.thestring = "amd64p32"; + thearch.thestring = "amd64p32"; } } @@ -65,61 +65,46 @@ betypeinit(void) void main(int argc, char **argv) { - arch.thechar = thechar; - arch.thestring = thestring; - arch.thelinkarch = thelinkarch; - arch.typedefs = typedefs; - arch.MAXWIDTH = MAXWIDTH; - arch.afunclit = afunclit; - arch.anyregalloc = anyregalloc; - arch.betypeinit = betypeinit; - arch.bgen = bgen; - arch.cgen = cgen; - arch.cgen_asop = cgen_asop; - arch.cgen_call = cgen_call; - arch.cgen_callinter = cgen_callinter; - arch.cgen_ret = cgen_ret; - arch.clearfat = clearfat; - arch.clearp = clearp; - arch.defframe = defframe; - arch.dgostringptr = dgostringptr; - arch.dgostrlitptr = dgostrlitptr; - arch.dsname = dsname; - arch.dsymptr = dsymptr; - arch.dumpdata = dumpdata; - arch.dumpit = dumpit; - arch.excise = excise; - arch.expandchecks = expandchecks; - arch.fixautoused = fixautoused; - arch.gclean = gclean; - arch.gdata = gdata; - arch.gdatacomplex = gdatacomplex; - arch.gdatastring = gdatastring; - arch.ggloblnod = ggloblnod; - arch.ggloblsym = ggloblsym; - arch.ginit = ginit; - arch.gins = gins; - arch.ginscall = ginscall; - arch.gjmp = gjmp; - arch.gtrack = gtrack; - arch.gused = gused; - arch.igen = igen; - arch.isfat = isfat; - arch.linkarchinit = linkarchinit; - arch.markautoused = markautoused; - arch.naddr = naddr; - arch.newplist = newplist; - arch.nodarg = nodarg; - arch.patch = patch; - arch.proginfo = proginfo; - arch.regalloc = regalloc; - arch.regfree = regfree; - arch.regopt = regopt; - arch.regtyp = regtyp; - arch.sameaddr = sameaddr; - arch.smallindir = smallindir; - arch.stackaddr = stackaddr; - arch.unpatch = unpatch; + thearch.thechar = thechar; + thearch.thestring = thestring; + thearch.thelinkarch = thelinkarch; + thearch.typedefs = typedefs; + thearch.REGSP = REGSP; + thearch.REGCTXT = REGCTXT; + thearch.MAXWIDTH = MAXWIDTH; + thearch.anyregalloc = anyregalloc; + thearch.betypeinit = betypeinit; + thearch.bgen = bgen; + thearch.cgen = cgen; + thearch.cgen_call = cgen_call; + thearch.cgen_callinter = cgen_callinter; + thearch.cgen_ret = cgen_ret; + thearch.clearfat = clearfat; + thearch.defframe = defframe; + thearch.excise = excise; + thearch.expandchecks = expandchecks; + thearch.gclean = gclean; + thearch.ginit = ginit; + thearch.gins = gins; + thearch.ginscall = ginscall; + thearch.igen = igen; + thearch.linkarchinit = linkarchinit; + thearch.peep = peep; + thearch.proginfo = proginfo; + thearch.regalloc = regalloc; + thearch.regfree = regfree; + thearch.regtyp = regtyp; + thearch.sameaddr = sameaddr; + thearch.smallindir = smallindir; + thearch.stackaddr = stackaddr; + thearch.excludedregs = excludedregs; + thearch.RtoB = RtoB; + thearch.FtoB = FtoB; + thearch.BtoR = BtoR; + thearch.BtoF = BtoF; + thearch.optoas = optoas; + thearch.doregbits = doregbits; + thearch.regnames = regnames; gcmain(argc, argv); } diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 7a09f673ef..a6dfad9c8e 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -9,10 +9,7 @@ #include "../gc/go.h" #include "../6l/6.out.h" -EXTERN int32 dynloc; EXTERN uchar reg[MAXREG]; -EXTERN int32 pcloc; // instruction counter -EXTERN Strlit emptystring; EXTERN Node* panicdiv; extern vlong unmappedzero; extern int addptr; @@ -117,7 +114,6 @@ int anyregalloc(void); void betypeinit(void); void bgen(Node*, int, int, Prog*); void cgen(Node*, Node*); -void cgen_asop(Node*); void cgen_call(Node*, int); void cgen_callinter(Node*, Node*, int); void cgen_ret(Node*); @@ -163,3 +159,18 @@ int smallindir(Addr*, Addr*); int stackaddr(Addr*); Prog* unpatch(Prog*); +/* + * reg.c + */ +uint64 excludedregs(void); +uint64 RtoB(int); +uint64 FtoB(int); +int BtoR(uint64); +int BtoF(uint64); +uint64 doregbits(int); +char** regnames(int*); + +/* + * peep.c + */ +void peep(Prog*); diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 9f4b4d4ae3..72104589a3 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -7,7 +7,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static Prog *appendpp(Prog*, int, int, int, vlong, int, int, vlong); static Prog *zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *ax); @@ -115,52 +115,6 @@ appendpp(Prog *p, int as, int ftype, int freg, vlong foffset, int ttype, int tre return q; } -// Sweep the prog list to mark any used nodes. -void -markautoused(Prog* p) -{ - for (; p; p = p->link) { - if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL) - continue; - - if (p->from.node) - ((Node*)(p->from.node))->used = 1; - - if (p->to.node) - ((Node*)(p->to.node))->used = 1; - } -} - -// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. -void -fixautoused(Prog *p) -{ - Prog **lp; - - for (lp=&p; (p=*lp) != P; ) { - if (p->as == ATYPE && p->from.node && p->from.name == NAME_AUTO && !((Node*)(p->from.node))->used) { - *lp = p->link; - continue; - } - if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !((Node*)(p->to.node))->used) { - // Cannot remove VARDEF instruction, because - unlike TYPE handled above - - // VARDEFs are interspersed with other code, and a jump might be using the - // VARDEF as a target. Replace with a no-op instead. A later pass will remove - // the no-ops. - nopout(p); - continue; - } - if (p->from.name == NAME_AUTO && p->from.node) - p->from.offset += ((Node*)(p->from.node))->stkdelta; - - if (p->to.name == NAME_AUTO && p->to.node) - p->to.offset += ((Node*)(p->to.node))->stkdelta; - - lp = &p->link; - } -} - - /* * generate: * call f @@ -478,159 +432,6 @@ cgen_ret(Node *n) } /* - * generate += *= etc. - */ -void -cgen_asop(Node *n) -{ - Node n1, n2, n3, n4; - Node *nl, *nr; - Prog *p1; - Addr addr; - int a; - - nl = n->left; - nr = n->right; - - if(nr->ullman >= UINF && nl->ullman >= UINF) { - tempname(&n1, nr->type); - cgen(nr, &n1); - n2 = *n; - n2.right = &n1; - cgen_asop(&n2); - goto ret; - } - - if(!isint[nl->type->etype]) - goto hard; - if(!isint[nr->type->etype]) - goto hard; - - switch(n->etype) { - case OADD: - if(smallintconst(nr)) - if(mpgetfix(nr->val.u.xval) == 1) { - a = optoas(OINC, nl->type); - if(nl->addable) { - gins(a, N, nl); - goto ret; - } - if(sudoaddable(a, nl, &addr)) { - p1 = gins(a, N, N); - p1->to = addr; - sudoclean(); - goto ret; - } - } - break; - - case OSUB: - if(smallintconst(nr)) - if(mpgetfix(nr->val.u.xval) == 1) { - a = optoas(ODEC, nl->type); - if(nl->addable) { - gins(a, N, nl); - goto ret; - } - if(sudoaddable(a, nl, &addr)) { - p1 = gins(a, N, N); - p1->to = addr; - sudoclean(); - goto ret; - } - } - break; - } - - switch(n->etype) { - case OADD: - case OSUB: - case OXOR: - case OAND: - case OOR: - a = optoas(n->etype, nl->type); - if(nl->addable) { - if(smallintconst(nr)) { - gins(a, nr, nl); - goto ret; - } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); - gins(a, &n2, nl); - regfree(&n2); - goto ret; - } - if(nr->ullman < UINF) - if(sudoaddable(a, nl, &addr)) { - if(smallintconst(nr)) { - p1 = gins(a, nr, N); - p1->to = addr; - sudoclean(); - goto ret; - } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); - p1 = gins(a, &n2, N); - p1->to = addr; - regfree(&n2); - sudoclean(); - goto ret; - } - } - -hard: - n2.op = 0; - n1.op = 0; - 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; - } else { - tempname(&n2, nr->type); - cgen(nr, &n2); - nr = &n2; - } - if(!nl->addable) { - igen(nl, &n1, N); - nl = &n1; - } - - n3 = *n; - n3.left = nl; - n3.right = nr; - n3.op = n->etype; - - regalloc(&n4, nl->type, N); - cgen(&n3, &n4); - gmove(&n4, nl); - - if(n1.op) - regfree(&n1); - if(n2.op == OREGISTER) - regfree(&n2); - regfree(&n4); - -ret: - ; -} - -int -samereg(Node *a, Node *b) -{ - if(a == N || b == N) - return 0; - if(a->op != OREGISTER) - return 0; - if(b->op != OREGISTER) - return 0; - if(a->val.u.reg != b->val.u.reg) - return 0; - return 1; -} - -/* * generate division. * generates one of: * res = nl / nr diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c deleted file mode 100644 index 33ee4d65c4..0000000000 --- a/src/cmd/6g/gobj.c +++ /dev/null @@ -1,244 +0,0 @@ -// Derived from Inferno utils/6c/swt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/swt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - -#include <u.h> -#include <libc.h> -#include "gg.h" - -int -dsname(Sym *s, int off, char *t, int n) -{ - Prog *p; - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.offset = off; - p->from.sym = linksym(s); - p->from3.type = TYPE_CONST; - p->from3.offset = n; - - p->to.type = TYPE_SCONST; - memmove(p->to.u.sval, t, n); - return off + n; -} - -/* - * make a refer to the data s, s+len - * emitting DATA if needed. - */ -void -datastring(char *s, int len, Addr *a) -{ - Sym *sym; - - sym = stringsym(s, len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->sym = linksym(sym); - a->node = sym->def; - a->offset = widthptr+widthint; // skip header - a->etype = simtype[TINT]; -} - -/* - * make a refer to the string sval, - * emitting DATA if needed. - */ -void -datagostring(Strlit *sval, Addr *a) -{ - Sym *sym; - - sym = stringsym(sval->s, sval->len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->sym = linksym(sym); - a->node = sym->def; - a->offset = 0; // header - a->etype = TSTRING; -} - -void -gdata(Node *nam, Node *nr, int wid) -{ - Prog *p; - - if(nr->op == OLITERAL) { - switch(nr->val.ctype) { - case CTCPLX: - gdatacomplex(nam, nr->val.u.cval); - return; - case CTSTR: - gdatastring(nam, nr->val.u.sval); - return; - } - } - p = gins(ADATA, nam, nr); - p->from3.type = TYPE_CONST; - p->from3.offset = wid; -} - -void -gdatacomplex(Node *nam, Mpcplx *cval) -{ - Prog *p; - int w; - - w = cplxsubtype(nam->type->etype); - w = types[w]->width; - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->real); - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->from.offset += w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->imag); -} - -void -gdatastring(Node *nam, Strlit *sval) -{ - Prog *p; - Node nod1; - - p = gins(ADATA, nam, N); - datastring(sval->s, sval->len, &p->to); - p->from3.type = TYPE_CONST; - p->from3.offset = types[tptr]->width; - p->to.type = TYPE_ADDR; -//print("%P\n", p); - - nodconst(&nod1, types[TINT], sval->len); - p = gins(ADATA, nam, &nod1); - p->from3.type = TYPE_CONST; - p->from3.offset = widthint; - p->from.offset += widthptr; -} - -int -dstringptr(Sym *s, int off, char *str) -{ - Prog *p; - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - - datastring(str, strlen(str)+1, &p->to); - p->to.type = TYPE_ADDR; - p->to.etype = simtype[TINT]; - off += widthptr; - - return off; -} - -int -dgostrlitptr(Sym *s, int off, Strlit *lit) -{ - Prog *p; - - if(lit == nil) - return duintptr(s, off, 0); - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - datagostring(lit, &p->to); - p->to.type = TYPE_ADDR; - p->to.etype = simtype[TINT]; - off += widthptr; - - return off; -} - -int -dgostringptr(Sym *s, int off, char *str) -{ - int n; - Strlit *lit; - - if(str == nil) - return duintptr(s, off, 0); - - n = strlen(str); - lit = mal(sizeof *lit + n); - strcpy(lit->s, str); - lit->len = n; - return dgostrlitptr(s, off, lit); -} - -int -dsymptr(Sym *s, int off, Sym *x, int xoff) -{ - Prog *p; - - off = rnd(off, widthptr); - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - p->to.type = TYPE_ADDR; - p->to.name = NAME_EXTERN; - p->to.sym = linksym(x); - p->to.offset = xoff; - off += widthptr; - - return off; -} - -void -nopout(Prog *p) -{ - p->as = ANOP; - p->from = zprog.from; - p->to = zprog.to; -} - diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 697016c6f3..55d3425081 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -37,225 +37,6 @@ // the text segment up higher in 6l for all GOOS. // At the same time, can raise StackBig in ../../runtime/stack.h. vlong unmappedzero = 4096; - -void -clearp(Prog *p) -{ - p->as = AEND; - p->from.type = TYPE_NONE; - p->from.index = TYPE_NONE; - p->to.type = TYPE_NONE; - p->to.index = TYPE_NONE; - p->pc = pcloc; - pcloc++; -} - -static int ddumped; -static Prog *dfirst; -static Prog *dpc; - -/* - * generate and return proc with p->as = as, - * linked into program. pc is next instruction. - */ -Prog* -prog(int as) -{ - Prog *p; - - if(as == ADATA || as == AGLOBL) { - if(ddumped) - fatal("already dumped data"); - if(dpc == nil) { - dpc = mal(sizeof(*dpc)); - dfirst = dpc; - } - p = dpc; - dpc = mal(sizeof(*dpc)); - p->link = dpc; - } else { - p = pc; - pc = mal(sizeof(*pc)); - clearp(pc); - p->link = pc; - } - - if(lineno == 0) { - if(debug['K']) - warn("prog: line 0"); - } - - p->as = as; - p->lineno = lineno; - return p; -} - -void -dumpdata(void) -{ - ddumped = 1; - if(dfirst == nil) - return; - newplist(); - *pc = *dfirst; - pc = dpc; - clearp(pc); -} - -/* - * 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, int likely) -{ - Prog *p; - - USED(t); - - p = prog(as); - p->to.type = TYPE_BRANCH; - p->to.u.branch = P; - if(as != AJMP && likely != 0) { - p->from.type = TYPE_CONST; - p->from.offset = likely > 0; - } - return p; -} - -/* - * patch previous branch to jump to to. - */ -void -patch(Prog *p, Prog *to) -{ - if(p->to.type != TYPE_BRANCH) - fatal("patch: not a branch"); - p->to.u.branch = to; - p->to.offset = to->pc; -} - -Prog* -unpatch(Prog *p) -{ - Prog *q; - - if(p->to.type != TYPE_BRANCH) - fatal("unpatch: not a branch"); - q = p->to.u.branch; - p->to.u.branch = P; - p->to.offset = 0; - return q; -} - -/* - * start a new Prog list. - */ -Plist* -newplist(void) -{ - Plist *pl; - - pl = linknewplist(ctxt); - - pc = mal(sizeof(*pc)); - clearp(pc); - pl->firstpc = pc; - - return pl; -} - -void -gused(Node *n) -{ - gins(ANOP, n, N); // used -} - -Prog* -gjmp(Prog *to) -{ - Prog *p; - - p = gbranch(AJMP, T, 0); - if(to != P) - patch(p, to); - return p; -} - -void -ggloblnod(Node *nam) -{ - Prog *p; - - p = gins(AGLOBL, nam, N); - p->lineno = nam->lineno; - p->from.sym->gotype = linksym(ngotype(nam)); - p->to.sym = nil; - p->to.type = TYPE_CONST; - p->to.offset = nam->type->width; - if(nam->readonly) - p->from3.offset = RODATA; - if(nam->type != T && !haspointers(nam->type)) - p->from3.offset |= NOPTR; -} - -void -gtrack(Sym *s) -{ - Prog *p; - - p = gins(AUSEFIELD, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); -} - -void -ggloblsym(Sym *s, int32 width, int8 flags) -{ - Prog *p; - - p = gins(AGLOBL, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->to.type = TYPE_CONST; - p->to.offset = width; - p->from3.offset = flags; -} - -int -isfat(Type *t) -{ - if(t != T) - switch(t->etype) { - case TSTRUCT: - case TARRAY: - case TSTRING: - case TINTER: // maybe remove later - return 1; - } - return 0; -} - -/* - * naddr of func generates code for address of func. - * if using opcode that can take address implicitly, - * call afunclit to fix up the argument. - */ -void -afunclit(Addr *a, Node *n) -{ - if(a->type == TYPE_ADDR && a->name == NAME_EXTERN) { - a->type = TYPE_MEM; - a->sym = linksym(n->sym); - } -} - static int resvd[] = { REG_DI, // for movstring @@ -314,7 +95,7 @@ gclean(void) yyerror("reg %R left allocated\n", i); } -int32 +int anyregalloc(void) { int i, j; @@ -422,105 +203,6 @@ regfree(Node *n) } /* - * initialize n to be register r of type t. - */ -void -nodreg(Node *n, Type *t, int r) -{ - if(t == T) - fatal("nodreg: t nil"); - - memset(n, 0, sizeof(*n)); - n->op = OREGISTER; - n->addable = 1; - ullmancalc(n); - n->val.u.reg = r; - n->type = t; -} - -/* - * initialize n to be indirect of register r; n is type t. - */ -void -nodindreg(Node *n, Type *t, int r) -{ - nodreg(n, t, r); - n->op = OINDREG; -} - -Node* -nodarg(Type *t, int fp) -{ - Node *n; - NodeList *l; - Type *first; - Iter savet; - - // entire argument struct, not just one arg - if(t->etype == TSTRUCT && t->funarg) { - n = nod(ONAME, N, N); - n->sym = lookup(".args"); - n->type = t; - first = structfirst(&savet, &t); - if(first == nil) - fatal("nodarg: bad struct"); - if(first->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = first->width; - n->addable = 1; - goto fp; - } - - if(t->etype != TFIELD) - fatal("nodarg: not field %T", t); - - if(fp == 1) { - for(l=curfn->dcl; l; l=l->next) { - n = l->n; - if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym) - return n; - } - } - - n = nod(ONAME, N, N); - n->type = t->type; - n->sym = t->sym; - - if(t->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = t->width; - n->addable = 1; - n->orig = t->nname; - -fp: - // Rewrite argument named _ to __, - // or else the assignment to _ will be - // discarded during code generation. - if(isblank(n)) - n->sym = lookup("__"); - - switch(fp) { - case 0: // output arg - n->op = OINDREG; - n->val.u.reg = REG_SP; - break; - - case 1: // input arg - n->class = PPARAM; - break; - - case 2: // offset output arg -fatal("shouldn't be used"); - n->op = OINDREG; - n->val.u.reg = REG_SP; - n->xoffset += types[tptr]->width; - break; - } - n->typecheck = 1; - return n; -} - -/* * generate * as $c, reg */ @@ -577,27 +259,6 @@ ginscon(int as, vlong c, Node *n2) /*c2go int CASE(int, int); */ /* - * Is this node a memory operand? - */ -int -ismem(Node *n) -{ - switch(n->op) { - case OITAB: - case OSPTR: - case OLEN: - case OCAP: - case OINDREG: - case ONAME: - case OPARAM: - case OCLOSUREVAR: - case OADDR: - return 1; - } - return 0; -} - -/* * set up nodes representing 2^63 */ Node bigi; @@ -1112,213 +773,6 @@ fixlargeoffset(Node *n) } /* - * generate code to compute n; - * make a refer to result. - */ -void -naddr(Node *n, Addr *a, int canemitcode) -{ - Sym *s; - - a->scale = 0; - a->reg = REG_NONE; - a->index = REG_NONE; - a->type = TYPE_NONE; - a->name = NAME_NONE; - a->gotype = nil; - 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); - break; - - case OREGISTER: - a->type = TYPE_REG; - a->reg = n->val.u.reg; - a->sym = nil; - break; - - case OINDREG: - a->type = TYPE_MEM; - a->reg = n->val.u.reg; - a->sym = linksym(n->sym); - a->offset = n->xoffset; - if(a->offset != (int32)a->offset) - yyerror("offset %lld too large for OINDREG", a->offset); - break; - - case OPARAM: - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - a->etype = simtype[n->left->type->etype]; - a->width = n->left->type->width; - a->offset = n->xoffset; - a->sym = linksym(n->left->sym); - a->type = TYPE_MEM; - a->name = NAME_PARAM; - a->node = n->left->orig; - break; - - case OCLOSUREVAR: - if(!curfn->needctxt) - fatal("closurevar without needctxt"); - a->type = TYPE_MEM; - a->reg = REG_DX; - a->sym = nil; - a->offset = n->xoffset; - break; - - case OCFUNC: - naddr(n->left, a, canemitcode); - a->sym = linksym(n->left->sym); - break; - - case ONAME: - a->etype = 0; - if(n->type != T) - a->etype = simtype[n->type->etype]; - a->offset = n->xoffset; - s = n->sym; - a->node = n->orig; - //if(a->node >= (Node*)&n) - // fatal("stack node"); - if(s == S) - s = lookup(".noname"); - if(n->method) { - if(n->type != T) - if(n->type->sym != S) - if(n->type->sym->pkg != nil) - s = pkglookup(s->name, n->type->sym->pkg); - } - - a->type = TYPE_MEM; - switch(n->class) { - default: - fatal("naddr: ONAME class %S %d\n", n->sym, n->class); - case PEXTERN: - a->name = NAME_EXTERN; - break; - case PAUTO: - a->name = NAME_AUTO; - break; - case PPARAM: - case PPARAMOUT: - a->name = NAME_PARAM; - break; - case PFUNC: - a->name = NAME_EXTERN; - a->type = TYPE_ADDR; - a->width = widthptr; - s = funcsym(s); - break; - } - a->sym = linksym(s); - break; - - case OLITERAL: - switch(n->val.ctype) { - default: - fatal("naddr: const %lT", n->type); - break; - case CTFLT: - a->type = TYPE_FCONST; - a->u.dval = mpgetflt(n->val.u.fval); - break; - case CTINT: - case CTRUNE: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = mpgetfix(n->val.u.xval); - break; - case CTSTR: - datagostring(n->val.u.sval, a); - break; - case CTBOOL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = n->val.u.bval; - break; - case CTNIL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = 0; - break; - } - break; - - case OADDR: - naddr(n->left, a, canemitcode); - a->width = widthptr; - if(a->type != TYPE_MEM) - fatal("naddr: OADDR %D", a); - a->type = TYPE_ADDR; - break; - - case OITAB: - // itable of interface value - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // itab(nil) - a->etype = tptr; - a->width = widthptr; - break; - - case OSPTR: - // pointer in a string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // ptr(nil) - a->etype = simtype[tptr]; - a->offset += Array_array; - a->width = widthptr; - break; - - case OLEN: - // len of string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->etype = simtype[TUINT]; - a->offset += Array_nel; - a->width = widthint; - break; - - case OCAP: - // cap of string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // cap(nil) - a->etype = simtype[TUINT]; - a->offset += Array_cap; - a->width = widthint; - break; - -// case OADD: -// if(n->right->op == OLITERAL) { -// v = n->right->vconst; -// naddr(n->left, a, canemitcode); -// } else -// if(n->left->op == OLITERAL) { -// v = n->left->vconst; -// naddr(n->right, a, canemitcode); -// } else -// goto bad; -// a->offset += v; -// break; - - } -} - -/* * return Axxx for Oxxx on type t. */ int @@ -1966,6 +1420,8 @@ sudoaddable(int as, Node *n, Addr *a) if(n->type == T) return 0; + memset(a, 0, sizeof *a); + switch(n->op) { case OLITERAL: if(!isconst(n, CTINT)) diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index e05a06087f..261cb6e0a1 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -31,7 +31,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static void conprop(Flow *r); static void elimshortmov(Graph *g); @@ -44,9 +44,15 @@ static int copy1(Adr*, Adr*, Flow*, int); static int copyas(Adr*, Adr*); static int copyau(Adr*, Adr*); static int copysub(Adr*, Adr*, Adr*, int); +static int copyu(Prog*, Adr*, Adr*); static uint32 gactive; +enum +{ + exregoffset = REG_R15, +}; + // do we need the carry bit static int needc(Prog *p) @@ -91,7 +97,7 @@ peep(Prog *firstp) Prog *p, *p1; int t; - g = flowstart(firstp, sizeof(Flow)); + g = flowstart(firstp, 0); if(g == nil) return; gactive = 0; @@ -737,7 +743,7 @@ copy1(Adr *v1, Adr *v2, Flow *r, int f) * 4 if set and used * 0 otherwise (not touched) */ -int +static int copyu(Prog *p, Adr *v, Adr *s) { ProgInfo info; @@ -761,7 +767,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ACALL: if(REGEXT && v->type == TYPE_REG && v->reg <= REGEXT && v->reg > exregoffset) return 2; - if(REGARG >= 0 && v->type == TYPE_REG && v->reg == (uchar)REGARG) + if(REGARG >= 0 && v->type == TYPE_REG && v->reg == REGARG) return 2; if(v->type == p->from.type && v->reg == p->from.reg) return 2; @@ -776,7 +782,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ATEXT: - if(REGARG >= 0 && v->type == TYPE_REG && v->reg == (uchar)REGARG) + if(REGARG >= 0 && v->type == TYPE_REG && v->reg == REGARG) return 3; return 0; } diff --git a/src/cmd/6g/prog.c b/src/cmd/6g/prog.c index 32d5256f8c..79b7911e5b 100644 --- a/src/cmd/6g/prog.c +++ b/src/cmd/6g/prog.c @@ -5,7 +5,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" // Matches real RtoB but can be used in global initializer. #define RtoB(r) (1<<((r)-REG_AX)) diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 7db44245f1..e01f265a13 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -31,56 +31,11 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" -#define NREGVAR 32 /* 16 general + 16 floating */ -#define REGBITS ((uint64)0xffffffffull) -/*c2go enum { +enum { NREGVAR = 32, - REGBITS = 0xffffffff, }; -*/ - -static Reg* firstr; -static int first = 1; - -int -rcmp(const void *a1, const void *a2) -{ - Rgn *p1, *p2; - int c1, c2; - - p1 = (Rgn*)a1; - p2 = (Rgn*)a2; - c1 = p2->cost; - c2 = p1->cost; - if(c1 -= c2) - return c1; - return p2->varno - p1->varno; -} - -static void -setaddrs(Bits bit) -{ - int i, n; - Var *v; - Node *node; - - while(bany(&bit)) { - // convert each bit to a variable - i = bnum(bit); - node = var[i].node; - n = var[i].name; - biclr(&bit, i); - - // disable all pieces of that variable - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node && v->name == n) - v->addr = 2; - } - } -} static char* regname[] = { ".AX", @@ -117,471 +72,23 @@ static char* regname[] = { ".X15", }; -static Node* regnodes[NREGVAR]; - -static void walkvardef(Node *n, Reg *r, int active); - -void -regopt(Prog *firstp) -{ - Reg *r, *r1; - Prog *p; - Graph *g; - ProgInfo info; - int i, z, active; - uint32 vreg; - Bits bit; - - if(first) { - fmtinstall('Q', Qconv); - exregoffset = REG_R15; - first = 0; - } - - mergetemp(firstp); - - /* - * control flow is more complicated in generated go code - * than in generated c code. define pseudo-variables for - * registers, so we have complete register usage information. - */ - nvar = NREGVAR; - memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) { - if(regnodes[i] == N) - regnodes[i] = newname(lookup(regname[i])); - var[i].node = regnodes[i]; - } - - regbits = RtoB(REG_SP); - for(z=0; z<BITS; z++) { - externs.b[z] = 0; - params.b[z] = 0; - consts.b[z] = 0; - addrs.b[z] = 0; - ivar.b[z] = 0; - ovar.b[z] = 0; - } - - /* - * pass 1 - * build aux data structure - * allocate pcs - * find use and set of variables - */ - g = flowstart(firstp, sizeof(Reg)); - if(g == nil) { - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - return; - } - - firstr = (Reg*)g->start; - - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF || p->as == AVARKILL) - continue; - proginfo(&info, p); - - // Avoid making variables for direct-called functions. - if(p->as == ACALL && p->to.type == TYPE_MEM && p->to.name == NAME_EXTERN) - continue; - - r->use1.b[0] |= info.reguse | info.regindex; - r->set.b[0] |= info.regset; - - bit = mkvar(r, &p->from); - if(bany(&bit)) { - if(info.flags & LeftAddr) - setaddrs(bit); - if(info.flags & LeftRead) - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - if(info.flags & LeftWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - - bit = mkvar(r, &p->to); - if(bany(&bit)) { - if(info.flags & RightAddr) - setaddrs(bit); - if(info.flags & RightRead) - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - if(info.flags & RightWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - } - - for(i=0; i<nvar; i++) { - Var *v = var+i; - if(v->addr) { - bit = blsh(i); - for(z=0; z<BITS; z++) - addrs.b[z] |= bit.b[z]; - } - - 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->f, 1); - - /* - * pass 2 - * find looping structure - */ - flowrpo(g); - - if(debug['R'] && debug['v']) - dumpit("pass2", &firstr->f, 1); - - /* - * pass 2.5 - * iterate propagating fat vardef covering forward - * r->act records vars with a VARDEF since the last CALL. - * (r->act will be reused in pass 5 for something else, - * but we'll be done with it by then.) - */ - active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->f.active = 0; - r->act = zbits; - } - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF && isfat(((Node*)(p->to.node))->type) && ((Node*)(p->to.node))->opt != nil) { - active++; - walkvardef(p->to.node, r, active); - } - } - - /* - * pass 3 - * iterate propagating usage - * back until flow graph is complete - */ -loop1: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - if(r->f.prog->as == ARET) - prop(r, zbits, zbits); -loop11: - /* pick up unreachable code */ - i = 0; - for(r = firstr; r != R; r = r1) { - r1 = (Reg*)r->f.link; - if(r1 && r1->f.active && !r->f.active) { - prop(r, zbits, zbits); - i = 1; - } - } - if(i) - goto loop11; - if(change) - goto loop1; - - if(debug['R'] && debug['v']) - dumpit("pass3", &firstr->f, 1); - - /* - * pass 4 - * iterate propagating register/variable synchrony - * forward until graph is complete - */ -loop2: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - synch(firstr, zbits); - if(change) - goto loop2; - - if(debug['R'] && debug['v']) - dumpit("pass4", &firstr->f, 1); - - /* - * pass 4.5 - * move register pseudo-variables into regu. - */ - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; - - r->set.b[0] &= ~REGBITS; - r->use1.b[0] &= ~REGBITS; - r->use2.b[0] &= ~REGBITS; - r->refbehind.b[0] &= ~REGBITS; - r->refahead.b[0] &= ~REGBITS; - r->calbehind.b[0] &= ~REGBITS; - r->calahead.b[0] &= ~REGBITS; - r->regdiff.b[0] &= ~REGBITS; - r->act.b[0] &= ~REGBITS; - } - - /* - * pass 5 - * isolate regions - * calculate costs (paint1) - */ - r = firstr; - if(r) { - for(z=0; z<BITS; z++) - bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & - ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->f.refset) { - // should never happen - all variables are preset - if(debug['w']) - print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - } - } - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->act = zbits; - rgp = region; - nregion = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - for(z=0; z<BITS; z++) - bit.b[z] = r->set.b[z] & - ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->f.refset) { - if(debug['w']) - print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - excise(&r->f); - } - for(z=0; z<BITS; z++) - bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); - while(bany(&bit)) { - i = bnum(bit); - rgp->enter = r; - rgp->varno = i; - change = 0; - paint1(r, i); - biclr(&bit, i); - if(change <= 0) - continue; - rgp->cost = change; - nregion++; - if(nregion >= NRGN) { - if(debug['R'] && debug['v']) - print("too many regions\n"); - goto brk; - } - rgp++; - } - } -brk: - qsort(region, nregion, sizeof(region[0]), rcmp); - - if(debug['R'] && debug['v']) - dumpit("pass5", &firstr->f, 1); - - /* - * pass 6 - * determine used registers (paint2) - * replace code (paint3) - */ - rgp = region; - if(debug['R'] && debug['v']) - print("\nregisterizing\n"); - for(i=0; i<nregion; i++) { - if(debug['R'] && debug['v']) - print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc); - bit = blsh(rgp->varno); - vreg = paint2(rgp->enter, rgp->varno, 0); - vreg = allreg(vreg, rgp); - if(rgp->regno != 0) { - if(debug['R'] && debug['v']) { - Var *v; - - v = var + rgp->varno; - print("registerize %N+%lld (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++; - } - - /* - * free aux structures. peep allocates new ones. - */ - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - flowend(g); - firstr = R; - - if(debug['R'] && debug['v']) { - // Rebuild flow graph, since we inserted instructions - g = flowstart(firstp, sizeof(Reg)); - firstr = (Reg*)g->start; - dumpit("pass6", &firstr->f, 1); - flowend(g); - firstr = R; - } - - /* - * pass 7 - * peep-hole on basic block - */ - if(!debug['R'] || debug['P']) - peep(firstp); - - /* - * eliminate nops - */ - for(p=firstp; p!=P; p=p->link) { - while(p->link != P && p->link->as == ANOP) - p->link = p->link->link; - if(p->to.type == TYPE_BRANCH) - while(p->to.u.branch != P && p->to.u.branch->as == ANOP) - p->to.u.branch = p->to.u.branch->link; - } - - if(debug['R']) { - if(ostats.ncvtreg || - ostats.nspill || - ostats.nreload || - ostats.ndelmov || - ostats.nvar || - ostats.naddr || - 0) - print("\nstats\n"); - - if(ostats.ncvtreg) - print(" %4d cvtreg\n", ostats.ncvtreg); - if(ostats.nspill) - print(" %4d spill\n", ostats.nspill); - if(ostats.nreload) - print(" %4d reload\n", ostats.nreload); - if(ostats.ndelmov) - print(" %4d delmov\n", ostats.ndelmov); - if(ostats.nvar) - print(" %4d var\n", ostats.nvar); - if(ostats.naddr) - print(" %4d addr\n", ostats.naddr); - - memset(&ostats, 0, sizeof(ostats)); - } -} - -static void -walkvardef(Node *n, Reg *r, int active) +char** +regnames(int *n) { - Reg *r1, *r2; - int bn; - Var *v; - - for(r1=r; r1!=R; r1=(Reg*)r1->f.s1) { - if(r1->f.active == active) - break; - r1->f.active = active; - if(r1->f.prog->as == AVARKILL && r1->f.prog->to.node == n) - break; - for(v=n->opt; v!=nil; v=v->nextinnode) { - bn = v - var; - biset(&r1->act, bn); - } - if(r1->f.prog->as == ACALL) - break; - } - - for(r2=r; r2!=r1; r2=(Reg*)r2->f.s1) - if(r2->f.s2 != nil) - walkvardef(n, (Reg*)r2->f.s2, active); + *n = NREGVAR; + return regname; } -/* - * add mov b,rn - * just after r - */ -void -addmove(Reg *r, int bn, int rn, int f) +uint64 +excludedregs(void) { - Prog *p, *p1; - Adr *a; - Var *v; - - p1 = mal(sizeof(*p1)); - clearp(p1); - p1->pc = 9999; - - p = r->f.prog; - p1->link = p->link; - p->link = p1; - p1->lineno = p->lineno; - - v = var + bn; - - a = &p1->to; - a->offset = v->offset; - a->etype = v->etype; - a->type = TYPE_MEM; - a->name = v->name; - a->node = v->node; - a->sym = linksym(v->node->sym); - - // need to clean this up with wptr and - // some of the defaults - p1->as = AMOVL; - switch(simtype[(uchar)v->etype]) { - default: - fatal("unknown type %E", v->etype); - case TINT8: - case TUINT8: - case TBOOL: - p1->as = AMOVB; - break; - case TINT16: - case TUINT16: - p1->as = AMOVW; - break; - case TINT64: - case TUINT64: - case TPTR64: - p1->as = AMOVQ; - break; - case TFLOAT32: - p1->as = AMOVSS; - break; - case TFLOAT64: - p1->as = AMOVSD; - break; - case TINT32: - case TUINT32: - case TPTR32: - break; - } - - p1->from.type = TYPE_REG; - p1->from.reg = rn; - p1->from.name = NAME_NONE; - if(!f) { - p1->from = *a; - *a = zprog.from; - a->type = TYPE_REG; - a->reg = rn; - if(v->etype == TUINT8) - p1->as = AMOVB; - if(v->etype == TUINT16) - p1->as = AMOVW; - } - if(debug['R'] && debug['v']) - print("%P ===add=== %P\n", p, p1); - ostats.nspill++; + return RtoB(REG_SP); } -uint32 +uint64 doregbits(int r) { - uint32 b; + uint64 b; b = 0; if(r >= REG_AX && r <= REG_R15) @@ -598,592 +105,19 @@ doregbits(int r) return b; } -static int -overlap(int64 o1, int w1, int64 o2, int w2) -{ - int64 t1, t2; - - t1 = o1+w1; - t2 = o2+w2; - - if(!(t1 > o2 && t2 > o1)) - return 0; - - return 1; -} - -Bits -mkvar(Reg *r, Adr *a) -{ - Var *v; - int i, n, et, z, flag; - int64 w; - uint32 regu; - int64 o; - Bits bit; - Node *node; - - /* - * mark registers used - */ - if(a->type == TYPE_NONE) - goto none; - - if(r != R) - r->use1.b[0] |= doregbits(a->index); - - switch(a->type) { - default: - regu = doregbits(a->reg); - if(regu == 0) - goto none; - bit = zbits; - bit.b[0] = regu; - return bit; - - case TYPE_ADDR: - a->type = TYPE_MEM; - bit = mkvar(r, a); - setaddrs(bit); - a->type = TYPE_ADDR; - ostats.naddr++; - goto none; - - case TYPE_MEM: - switch(a->name) { - default: - goto none; - case NAME_EXTERN: - case NAME_STATIC: - case NAME_PARAM: - case NAME_AUTO: - n = a->name; - break; - } - } - - node = a->node; - if(node == N || node->op != ONAME || node->orig == N) - goto none; - node = node->orig; - if(node->orig != node) - fatal("%D: bad node", a); - if(node->sym == S || node->sym->name[0] == '.') - goto none; - 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++) { - v = var+i; - if(v->node == node && v->name == n) { - if(v->offset == o) - if(v->etype == et) - if(v->width == w) - return blsh(i); - - // if they overlaps, disable both - if(overlap(v->offset, v->width, o, w)) { -// print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); - v->addr = 1; - flag = 1; - } - } - } - switch(et) { - case 0: - case TFUNC: - goto none; - } - - if(nvar >= NVAR) { - if(debug['w'] > 1 && node != N) - fatal("variable not optimized: %#N", node); - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node) - v->addr = 1; - } - goto none; - } - - i = nvar; - nvar++; - v = var+i; - v->offset = o; - v->name = n; - v->etype = et; - v->width = w; - v->addr = flag; // funny punning - v->node = node; - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v->nextinnode = node->opt; - node->opt = v; - - bit = blsh(i); - if(n == NAME_EXTERN || n == NAME_STATIC) - for(z=0; z<BITS; z++) - externs.b[z] |= bit.b[z]; - if(n == NAME_PARAM) - for(z=0; z<BITS; z++) - params.b[z] |= bit.b[z]; - - if(node->class == PPARAM) - for(z=0; z<BITS; z++) - ivar.b[z] |= bit.b[z]; - if(node->class == PPARAMOUT) - for(z=0; z<BITS; z++) - ovar.b[z] |= bit.b[z]; - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in ../gc/plive.c does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.c:/^typecheckclosure and dcl.c:/^oldname. - if(node->addrtaken) - v->addr = 1; - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if(node->class == PEXTERN || (hasdefer && node->class == PPARAMOUT)) - v->addr = 1; - - if(debug['R']) - print("bit=%2d et=%2E w=%lld+%lld %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); - ostats.nvar++; - - return bit; - -none: - return zbits; -} - -void -prop(Reg *r, Bits ref, Bits cal) -{ - Reg *r1, *r2; - int z, i, j; - Var *v, *v1; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { - for(z=0; z<BITS; z++) { - ref.b[z] |= r1->refahead.b[z]; - if(ref.b[z] != r1->refahead.b[z]) { - r1->refahead.b[z] = ref.b[z]; - change++; - } - cal.b[z] |= r1->calahead.b[z]; - if(cal.b[z] != r1->calahead.b[z]) { - r1->calahead.b[z] = cal.b[z]; - change++; - } - } - switch(r1->f.prog->as) { - case ACALL: - if(noreturn(r1->f.prog)) - break; - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for(z=0; z<BITS; z++) { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1->act.b[z]; - ref.b[z] = 0; - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for(z=0; z<BITS; z++) { - if(cal.b[z] == 0) - continue; - for(i=0; i<64; i++) { - if(z*64+i >= nvar || ((cal.b[z]>>i)&1) == 0) - continue; - v = var+z*64+i; - if(v->node->opt == nil) // v represents fixed register, not Go variable - continue; - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1 = v->node->opt; - j = v1 - var; - if(v == v1 || !btest(&cal, j)) { - for(; v1 != nil; v1 = v1->nextinnode) { - j = v1 - var; - biset(&cal, j); - } - } - } - } - break; - - case ATEXT: - for(z=0; z<BITS; z++) { - cal.b[z] = 0; - ref.b[z] = 0; - } - break; - - case ARET: - for(z=0; z<BITS; z++) { - cal.b[z] = externs.b[z] | ovar.b[z]; - ref.b[z] = 0; - } - break; - } - for(z=0; z<BITS; z++) { - ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | - r1->use1.b[z] | r1->use2.b[z]; - cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); - r1->refbehind.b[z] = ref.b[z]; - r1->calbehind.b[z] = cal.b[z]; - } - if(r1->f.active) - break; - r1->f.active = 1; - } - for(; r != r1; r = (Reg*)r->f.p1) - for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) - prop(r2, r->refbehind, r->calbehind); -} - -void -synch(Reg *r, Bits dif) -{ - Reg *r1; - int z; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { - for(z=0; z<BITS; z++) { - dif.b[z] = (dif.b[z] & - ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | - r1->set.b[z] | r1->regdiff.b[z]; - if(dif.b[z] != r1->regdiff.b[z]) { - r1->regdiff.b[z] = dif.b[z]; - change++; - } - } - if(r1->f.active) - break; - r1->f.active = 1; - for(z=0; z<BITS; z++) - dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->f.s2 != nil) - synch((Reg*)r1->f.s2, dif); - } -} - -uint32 -allreg(uint32 b, Rgn *r) -{ - Var *v; - int i; - - v = var + r->varno; - r->regno = 0; - switch(v->etype) { - - default: - fatal("unknown etype %d/%E", bitno(b), v->etype); - break; - - 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 TPTR32: - case TPTR64: - i = BtoR(~b); - if(i && r->cost > 0) { - r->regno = i; - return RtoB(i); - } - break; - - case TFLOAT32: - case TFLOAT64: - i = BtoF(~b); - if(i && r->cost > 0) { - r->regno = i; - return FtoB(i); - } - break; - } - return 0; -} - -void -paint1(Reg *r, int bn) -{ - Reg *r1; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL<<(bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->f.loop; - } - for(;;) { - r->act.b[z] |= bb; - - if(r->f.prog->as != ANOP) { // don't give credit for NOPs - if(r->use1.b[z] & bb) - change += CREF * r->f.loop; - if((r->use2.b[z]|r->set.b[z]) & bb) - change += CREF * r->f.loop; - } - - if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->f.loop; - } - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint1(r1, bn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint1(r1, bn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -uint32 -paint2(Reg *r, int bn, int depth) -{ - Reg *r1; - int z; - uint64 bb, vreg; - - z = bn/64; - bb = 1LL << (bn%64); - vreg = regbits; - if(!(r->act.b[z] & bb)) - return vreg; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(!(r1->act.b[z] & bb)) - break; - r = r1; - } - for(;;) { - if(debug['R'] && debug['v']) - print(" paint2 %d %P\n", depth, r->f.prog); - - r->act.b[z] &= ~bb; - - vreg |= r->regu; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(!(r->act.b[z] & bb)) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } - - return vreg; -} - -void -paint3(Reg *r, int bn, uint32 rb, int rn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL << (bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) - addmove(r, bn, rn, 0); - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - if(r->use1.b[z] & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->from, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->to, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - - if(STORE(r) & r->regdiff.b[z] & bb) - addmove(r, bn, rn, 1); - r->regu |= rb; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint3(r1, bn, rb, rn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint3(r1, bn, rb, rn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -void -addreg(Adr *a, int rn) -{ - a->sym = nil; - a->node = nil; - a->offset = 0; - a->type = TYPE_REG; - a->reg = rn; - a->name = 0; - - ostats.ncvtreg++; -} - -uint32 +uint64 RtoB(int r) { if(r < REG_AX || r > REG_R15) return 0; - return 1L << (r-REG_AX); + return 1ULL << (r-REG_AX); } int -BtoR(uint32 b) +BtoR(uint64 b) { - b &= 0xffffL; + b &= 0xffffULL; if(nacl) b &= ~((1<<(REG_BP-REG_AX)) | (1<<(REG_R15-REG_AX))); else if(framepointer_enabled) @@ -1200,16 +134,16 @@ BtoR(uint32 b) * ... * 31 X15 */ -uint32 +uint64 FtoB(int f) { if(f < REG_X0 || f > REG_X15) return 0; - return 1L << (f - REG_X0 + 16); + return 1ULL << (f - REG_X0 + 16); } int -BtoF(uint32 b) +BtoF(uint64 b) { b &= 0xFFFF0000L; @@ -1217,77 +151,3 @@ BtoF(uint32 b) return 0; return bitno(b) - 16 + REG_X0; } - -void -dumpone(Flow *f, int isreg) -{ - int z; - Bits bit; - Reg *r; - - print("%d:%P", f->loop, f->prog); - if(isreg) { - r = (Reg*)f; - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); - } - } - print("\n"); -} - -void -dumpit(char *str, Flow *r0, int isreg) -{ - Flow *r, *r1; - - print("\n%s\n", str); - for(r = r0; r != nil; r = r->link) { - dumpone(r, isreg); - r1 = r->p2; - if(r1 != nil) { - print(" pred:"); - for(; r1 != nil; r1 = r1->p2link) - print(" %.4ud", (int)r1->prog->pc); - print("\n"); - } - // Print successors if it's not just the next one - if(r->s1 != r->link || r->s2 != nil) { - print(" succ:"); - if(r->s1 != nil) - print(" %.4ud", (int)r->s1->prog->pc); - if(r->s2 != nil) - print(" %.4ud", (int)r->s2->prog->pc); - print("\n"); - } - } -} diff --git a/src/cmd/6l/6.out.h b/src/cmd/6l/6.out.h index 01d9bb7535..e25c6efec8 100644 --- a/src/cmd/6l/6.out.h +++ b/src/cmd/6l/6.out.h @@ -834,6 +834,7 @@ enum FREGRET = REG_X0, REGSP = REG_SP, REGTMP = REG_DI, + REGCTXT = REG_DX, REGEXT = REG_R15, /* compiler allocates external registers R15 down */ FREGMIN = REG_X0+5, /* first register variable */ FREGEXT = REG_X0+15, /* first external register */ diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 032c1eea09..a983373fa0 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -39,13 +39,6 @@ #define PADDR(a) ((uint32)(a) & ~0x80000000) -char linuxdynld[] = "/lib64/ld-linux-x86-64.so.2"; -char freebsddynld[] = "/libexec/ld-elf.so.1"; -char openbsddynld[] = "/usr/libexec/ld.so"; -char netbsddynld[] = "/libexec/ld.elf_so"; -char dragonflydynld[] = "/usr/libexec/ld-elf.so.2"; -char solarisdynld[] = "/lib/amd64/ld.so.1"; - char zeroes[32]; static int @@ -68,8 +61,6 @@ needlib(char *name) return 0; } -int nelfsym = 1; - static void addpltsym(LSym*); static void addgotsym(LSym*); @@ -236,7 +227,7 @@ adddynrel(LSym *s, Reloc *r) r->type = 256; // ignore during relocsym return; } - if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) { + if(HEADTYPE == Hdarwin && s->size == thearch.ptrsize && r->off == 0) { // Mach-O relocations are a royal pain to lay out. // They use a compact stateful bytecode representation // that is too much bother to deal with. @@ -271,7 +262,7 @@ elfreloc1(Reloc *r, vlong sectoff) { int32 elfsym; - VPUT(sectoff); + thearch.vput(sectoff); elfsym = r->xsym->elfsym; switch(r->type) { @@ -280,16 +271,16 @@ elfreloc1(Reloc *r, vlong sectoff) case R_ADDR: if(r->siz == 4) - VPUT(R_X86_64_32 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_32 | (uint64)elfsym<<32); else if(r->siz == 8) - VPUT(R_X86_64_64 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_64 | (uint64)elfsym<<32); else return -1; break; case R_TLS_LE: if(r->siz == 4) - VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_TPOFF32 | (uint64)elfsym<<32); else return -1; break; @@ -297,16 +288,16 @@ elfreloc1(Reloc *r, vlong sectoff) case R_CALL: if(r->siz == 4) { if(r->xsym->type == SDYNIMPORT) - VPUT(R_X86_64_GOTPCREL | (uint64)elfsym<<32); + thearch.vput(R_X86_64_GOTPCREL | (uint64)elfsym<<32); else - VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_PC32 | (uint64)elfsym<<32); } else return -1; break; case R_PCREL: if(r->siz == 4) { - VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_PC32 | (uint64)elfsym<<32); } else return -1; break; @@ -314,15 +305,15 @@ elfreloc1(Reloc *r, vlong sectoff) case R_TLS: if(r->siz == 4) { if(flag_shared) - VPUT(R_X86_64_GOTTPOFF | (uint64)elfsym<<32); + thearch.vput(R_X86_64_GOTTPOFF | (uint64)elfsym<<32); else - VPUT(R_X86_64_TPOFF32 | (uint64)elfsym<<32); + thearch.vput(R_X86_64_TPOFF32 | (uint64)elfsym<<32); } else return -1; break; } - VPUT(r->xadd); + thearch.vput(r->xadd); return 0; } @@ -382,8 +373,8 @@ machoreloc1(Reloc *r, vlong sectoff) break; } - LPUT(sectoff); - LPUT(v); + thearch.lput(sectoff); + thearch.lput(v); return 0; } @@ -798,18 +789,3 @@ asmb(void) } cflush(); } - -vlong -rnd(vlong v, vlong r) -{ - vlong c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} diff --git a/src/cmd/6l/l.h b/src/cmd/6l/l.h index 24eaa453dd..acc97d93d4 100644 --- a/src/cmd/6l/l.h +++ b/src/cmd/6l/l.h @@ -32,7 +32,6 @@ #include <libc.h> #include <bio.h> #include <link.h> -#include "6.out.h" #ifndef EXTERN #define EXTERN extern @@ -42,49 +41,14 @@ enum { thechar = '6', MaxAlign = 32, // max data alignment - - // 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 }; -EXTERN int PtrSize; -EXTERN int IntSize; -EXTERN int RegSize; - -#define P ((Prog*)0) -#define S ((LSym*)0) enum { MINLC = 1, }; -#pragma varargck type "I" uchar* - -EXTERN LSym* datap; -EXTERN int debug[128]; -EXTERN char literal[32]; -EXTERN int32 lcsize; -EXTERN char* rpath; -EXTERN int32 spsize; -EXTERN LSym* symlist; -EXTERN int32 symsize; - -int Iconv(Fmt *fp); void adddynlib(char *lib); void adddynrel(LSym *s, Reloc *r); void adddynrela(LSym *rela, LSym *s, Reloc *r); @@ -96,12 +60,6 @@ int elfreloc1(Reloc *r, vlong sectoff); void elfsetupplt(void); void listinit(void); int machoreloc1(Reloc *r, vlong sectoff); -vlong rnd(vlong v, vlong r); - -/* Native is little-endian */ -#define LPUT(a) lputl(a) -#define WPUT(a) wputl(a) -#define VPUT(a) vputl(a) /* Used by ../ld/dwarf.c */ enum diff --git a/src/cmd/6l/list.c b/src/cmd/6l/list.c index d960fcc915..6c4ea79f91 100644 --- a/src/cmd/6l/list.c +++ b/src/cmd/6l/list.c @@ -37,31 +37,4 @@ void listinit(void) { listinit6(); - fmtinstall('I', Iconv); -} - -int -Iconv(Fmt *fp) -{ - int i, n; - uchar *p; - char *s; - Fmt fmt; - - n = fp->prec; - fp->prec = 0; - if(!(fp->flags&FmtPrec) || n < 0) - return fmtstrcpy(fp, "%I"); - fp->flags &= ~FmtPrec; - p = va_arg(fp->args, uchar*); - - // format into temporary buffer and - // call fmtstrcpy to handle padding. - fmtstrinit(&fmt); - for(i=0; i<n; i++) - fmtprint(&fmt, "%.2ux", *p++); - s = fmtstrflush(&fmt); - fmtstrcpy(fp, s); - free(s); - return 0; } diff --git a/src/cmd/6l/obj.c b/src/cmd/6l/obj.c index 3b8e8f4d7a..0a2b2cb59e 100644 --- a/src/cmd/6l/obj.c +++ b/src/cmd/6l/obj.c @@ -38,17 +38,52 @@ #include "../ld/pe.h" #include <ar.h> -char* thestring = "amd64"; -LinkArch* thelinkarch = &linkamd64; +void +main(int argc, char **argv) +{ + linkarchinit(); + ldmain(argc, argv); +} void linkarchinit(void) { + thestring = "amd64"; + thelinkarch = &linkamd64; if(strcmp(getgoarch(), "amd64p32") == 0) thelinkarch = &linkamd64p32; - PtrSize = thelinkarch->ptrsize; - IntSize = PtrSize; - RegSize = thelinkarch->regsize; + + thearch.thechar = thechar; + thearch.ptrsize = thelinkarch->ptrsize; + thearch.intsize = thelinkarch->ptrsize; + thearch.regsize = thelinkarch->regsize; + thearch.funcalign = FuncAlign; + thearch.maxalign = MaxAlign; + thearch.minlc = MINLC; + thearch.dwarfregsp = DWARFREGSP; + + thearch.adddynlib = adddynlib; + thearch.adddynrel = adddynrel; + thearch.adddynsym = adddynsym; + thearch.archinit = archinit; + thearch.archreloc = archreloc; + thearch.archrelocvariant = archrelocvariant; + thearch.asmb = asmb; + thearch.elfreloc1 = elfreloc1; + thearch.elfsetupplt = elfsetupplt; + thearch.gentext = gentext; + thearch.listinit = listinit; + thearch.machoreloc1 = machoreloc1; + thearch.lput = lputl; + thearch.wput = wputl; + thearch.vput = vputl; + + thearch.linuxdynld = "/lib64/ld-linux-x86-64.so.2"; + thearch.freebsddynld = "/libexec/ld-elf.so.1"; + thearch.openbsddynld = "/usr/libexec/ld.so"; + thearch.netbsddynld = "/libexec/ld.elf_so"; + thearch.dragonflydynld = "/usr/libexec/ld-elf.so.2"; + thearch.solarisdynld = "/lib/amd64/ld.so.1"; } void diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index eabf52ae81..f06927c905 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -956,17 +956,7 @@ bgen(Node *n, int true, int likely, Prog *to) switch(n->op) { default: - def: - regalloc(&n1, n->type, N); - cgen(n, &n1); - nodconst(&n2, n->type, 0); - gins(optoas(OCMP, n->type), &n1, &n2); - a = AJNE; - if(!true) - a = AJEQ; - patch(gbranch(a, n->type, likely), to); - regfree(&n1); - return; + goto def; case OLITERAL: // need to ask if it is bool? @@ -986,27 +976,20 @@ bgen(Node *n, int true, int likely, Prog *to) return; case OANDAND: - if(!true) - goto caseor; - - caseand: - p1 = gbranch(AJMP, T, 0); - p2 = gbranch(AJMP, T, 0); - patch(p1, pc); - bgen(n->left, !true, -likely, p2); - bgen(n->right, !true, -likely, p2); - p1 = gbranch(AJMP, T, 0); - patch(p1, to); - patch(p2, pc); - return; - case OOROR: - if(!true) - goto caseand; - - caseor: - bgen(n->left, true, likely, to); - bgen(n->right, true, likely, to); + if((n->op == OANDAND) == true) { + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); + patch(p1, to); + patch(p2, pc); + } else { + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); + } return; case OEQ: @@ -1150,6 +1133,19 @@ cmp: regfree(nr); break; } + return; + +def: + regalloc(&n1, n->type, N); + cgen(n, &n1); + nodconst(&n2, n->type, 0); + gins(optoas(OCMP, n->type), &n1, &n2); + a = AJNE; + if(!true) + a = AJEQ; + patch(gbranch(a, n->type, likely), to); + regfree(&n1); + return; } /* @@ -1213,7 +1209,7 @@ stkof(Node *n) void sgen(Node *n, Node *res, int64 w) { - Node dst, src, tdst, tsrc; + Node dst, src, tdst, tsrc, cx; int32 c, q, odst, osrc; NodeList *l; Prog *p; @@ -1329,6 +1325,19 @@ sgen(Node *n, Node *res, int64 w) p->to.sym = linksym(pkglookup("duffcopy", runtimepkg)); // 10 and 128 = magic constants: see ../../runtime/asm_386.s p->to.offset = 10*(128-q); + } else if(!nacl && c == 0) { + nodreg(&cx, types[TINT32], REG_CX); + // We don't need the MOVSL side-effect of updating SI and DI, + // and issuing a sequence of MOVLs directly is faster. + src.op = OINDREG; + dst.op = OINDREG; + while(q > 0) { + gmove(&src, &cx); // MOVL x+(SI),CX + gmove(&cx, &dst); // MOVL CX,x+(DI) + src.xoffset += 4; + dst.xoffset += 4; + q--; + } } else while(q > 0) { gins(AMOVSL, N, N); // MOVL *(SI)+,*(DI)+ @@ -1360,13 +1369,15 @@ cadable(Node *n) /* * 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 can't. */ int componentgen(Node *nr, Node *nl) { - Node nodl, nodr; + Node nodl, nodr, tmp; Type *t; int freel, freer; vlong fldcount; @@ -1414,7 +1425,7 @@ componentgen(Node *nr, Node *nl) nodl = *nl; if(!cadable(nl)) { - if(nr == N || !cadable(nr)) + if(nr != N && !cadable(nr)) goto no; igen(nl, &nodl, N); freel = 1; @@ -1426,6 +1437,12 @@ componentgen(Node *nr, Node *nl) 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; } // nl and nr are 'cadable' which basically means they are names (variables) now. @@ -1462,8 +1479,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1472,8 +1488,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1482,8 +1497,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_cap-Array_nel; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1497,8 +1511,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1507,8 +1520,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1522,8 +1534,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1532,8 +1543,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; diff --git a/src/cmd/8g/galign.c b/src/cmd/8g/galign.c index 83c323516d..33951adfd2 100644 --- a/src/cmd/8g/galign.c +++ b/src/cmd/8g/galign.c @@ -42,61 +42,46 @@ betypeinit(void) void main(int argc, char **argv) { - arch.thechar = thechar; - arch.thestring = thestring; - arch.thelinkarch = thelinkarch; - arch.typedefs = typedefs; - arch.MAXWIDTH = MAXWIDTH; - arch.afunclit = afunclit; - arch.anyregalloc = anyregalloc; - arch.betypeinit = betypeinit; - arch.bgen = bgen; - arch.cgen = cgen; - arch.cgen_asop = cgen_asop; - arch.cgen_call = cgen_call; - arch.cgen_callinter = cgen_callinter; - arch.cgen_ret = cgen_ret; - arch.clearfat = clearfat; - arch.clearp = clearp; - arch.defframe = defframe; - arch.dgostringptr = dgostringptr; - arch.dgostrlitptr = dgostrlitptr; - arch.dsname = dsname; - arch.dsymptr = dsymptr; - arch.dumpdata = dumpdata; - arch.dumpit = dumpit; - arch.excise = excise; - arch.expandchecks = expandchecks; - arch.fixautoused = fixautoused; - arch.gclean = gclean; - arch.gdata = gdata; - arch.gdatacomplex = gdatacomplex; - arch.gdatastring = gdatastring; - arch.ggloblnod = ggloblnod; - arch.ggloblsym = ggloblsym; - arch.ginit = ginit; - arch.gins = gins; - arch.ginscall = ginscall; - arch.gjmp = gjmp; - arch.gtrack = gtrack; - arch.gused = gused; - arch.igen = igen; - arch.isfat = isfat; - arch.linkarchinit = linkarchinit; - arch.markautoused = markautoused; - arch.naddr = naddr; - arch.newplist = newplist; - arch.nodarg = nodarg; - arch.patch = patch; - arch.proginfo = proginfo; - arch.regalloc = regalloc; - arch.regfree = regfree; - arch.regopt = regopt; - arch.regtyp = regtyp; - arch.sameaddr = sameaddr; - arch.smallindir = smallindir; - arch.stackaddr = stackaddr; - arch.unpatch = unpatch; + thearch.thechar = thechar; + thearch.thestring = thestring; + thearch.thelinkarch = thelinkarch; + thearch.typedefs = typedefs; + thearch.REGSP = REGSP; + thearch.REGCTXT = REGCTXT; + thearch.MAXWIDTH = MAXWIDTH; + thearch.anyregalloc = anyregalloc; + thearch.betypeinit = betypeinit; + thearch.bgen = bgen; + thearch.cgen = cgen; + thearch.cgen_call = cgen_call; + thearch.cgen_callinter = cgen_callinter; + thearch.cgen_ret = cgen_ret; + thearch.clearfat = clearfat; + thearch.defframe = defframe; + thearch.excise = excise; + thearch.expandchecks = expandchecks; + thearch.gclean = gclean; + thearch.ginit = ginit; + thearch.gins = gins; + thearch.ginscall = ginscall; + thearch.igen = igen; + thearch.linkarchinit = linkarchinit; + thearch.peep = peep; + thearch.proginfo = proginfo; + thearch.regalloc = regalloc; + thearch.regfree = regfree; + thearch.regtyp = regtyp; + thearch.sameaddr = sameaddr; + thearch.smallindir = smallindir; + thearch.stackaddr = stackaddr; + thearch.excludedregs = excludedregs; + thearch.RtoB = RtoB; + thearch.FtoB = FtoB; + thearch.BtoR = BtoR; + thearch.BtoF = BtoF; + thearch.optoas = optoas; + thearch.doregbits = doregbits; + thearch.regnames = regnames; gcmain(argc, argv); } diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index b2b1178a52..872d946592 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -17,10 +17,7 @@ enum Fpop2 = 1<<2, }; -EXTERN int32 dynloc; EXTERN uchar reg[MAXREG]; -EXTERN int32 pcloc; // instruction counter -EXTERN Strlit emptystring; EXTERN Node* panicdiv; extern uint32 unmappedzero; @@ -130,7 +127,6 @@ int anyregalloc(void); void betypeinit(void); void bgen(Node*, int, int, Prog*); void cgen(Node*, Node*); -void cgen_asop(Node*); void cgen_call(Node*, int); void cgen_callinter(Node*, Node*, int); void cgen_ret(Node*); @@ -175,3 +171,19 @@ int sameaddr(Addr*, Addr*); int smallindir(Addr*, Addr*); int stackaddr(Addr*); Prog* unpatch(Prog*); + +/* + * reg.c + */ +uint64 excludedregs(void); +uint64 RtoB(int); +uint64 FtoB(int); +int BtoR(uint64); +int BtoF(uint64); +uint64 doregbits(int); +char** regnames(int*); + +/* + * peep.c + */ +void peep(Prog*); diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 2fd4a601ff..8188348282 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -7,7 +7,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static Prog *appendpp(Prog*, int, int, int, vlong, int, int, vlong); static Prog *zerorange(Prog *p, vlong frame, vlong lo, vlong hi, uint32 *ax); @@ -106,52 +106,6 @@ appendpp(Prog *p, int as, int ftype, int freg, vlong foffset, int ttype, int tre return q; } -// Sweep the prog list to mark any used nodes. -void -markautoused(Prog* p) -{ - for (; p; p = p->link) { - if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL) - continue; - - if (p->from.node) - ((Node*)(p->from.node))->used = 1; - - if (p->to.node) - ((Node*)(p->to.node))->used = 1; - } -} - -// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. -void -fixautoused(Prog* p) -{ - Prog **lp; - - for (lp=&p; (p=*lp) != P; ) { - if (p->as == ATYPE && p->from.node && p->from.name == NAME_AUTO && !((Node*)(p->from.node))->used) { - *lp = p->link; - continue; - } - if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !((Node*)(p->to.node))->used) { - // Cannot remove VARDEF instruction, because - unlike TYPE handled above - - // VARDEFs are interspersed with other code, and a jump might be using the - // VARDEF as a target. Replace with a no-op instead. A later pass will remove - // the no-ops. - nopout(p); - continue; - } - - if (p->from.type == TYPE_MEM && p->from.name == NAME_AUTO && p->from.node) - p->from.offset += ((Node*)(p->from.node))->stkdelta; - - if (p->to.type == TYPE_MEM && p->to.name == NAME_AUTO && p->to.node) - p->to.offset += ((Node*)(p->to.node))->stkdelta; - - lp = &p->link; - } -} - void clearfat(Node *nl) { @@ -525,154 +479,6 @@ cgen_ret(Node *n) } /* - * generate += *= etc. - */ -void -cgen_asop(Node *n) -{ - Node n1, n2, n3, n4; - Node *nl, *nr; - Prog *p1; - Addr addr; - int a; - - nl = n->left; - nr = n->right; - - if(nr->ullman >= UINF && nl->ullman >= UINF) { - tempname(&n1, nr->type); - cgen(nr, &n1); - n2 = *n; - n2.right = &n1; - cgen_asop(&n2); - goto ret; - } - - if(!isint[nl->type->etype]) - goto hard; - if(!isint[nr->type->etype]) - goto hard; - if(is64(nl->type) || is64(nr->type)) - goto hard; - - switch(n->etype) { - case OADD: - if(smallintconst(nr)) - if(mpgetfix(nr->val.u.xval) == 1) { - a = optoas(OINC, nl->type); - if(nl->addable) { - gins(a, N, nl); - goto ret; - } - if(sudoaddable(a, nl, &addr)) { - p1 = gins(a, N, N); - p1->to = addr; - sudoclean(); - goto ret; - } - } - break; - - case OSUB: - if(smallintconst(nr)) - if(mpgetfix(nr->val.u.xval) == 1) { - a = optoas(ODEC, nl->type); - if(nl->addable) { - gins(a, N, nl); - goto ret; - } - if(sudoaddable(a, nl, &addr)) { - p1 = gins(a, N, N); - p1->to = addr; - sudoclean(); - goto ret; - } - } - break; - } - - switch(n->etype) { - case OADD: - case OSUB: - case OXOR: - case OAND: - case OOR: - a = optoas(n->etype, nl->type); - if(nl->addable) { - if(smallintconst(nr)) { - gins(a, nr, nl); - goto ret; - } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); - gins(a, &n2, nl); - regfree(&n2); - goto ret; - } - if(nr->ullman < UINF) - if(sudoaddable(a, nl, &addr)) { - if(smallintconst(nr)) { - p1 = gins(a, nr, N); - p1->to = addr; - sudoclean(); - goto ret; - } - regalloc(&n2, nr->type, N); - cgen(nr, &n2); - p1 = gins(a, &n2, N); - p1->to = addr; - regfree(&n2); - sudoclean(); - goto ret; - } - } - -hard: - n2.op = 0; - n1.op = 0; - if(nr->ullman >= nl->ullman || nl->addable) { - mgen(nr, &n2, N); - nr = &n2; - } else { - tempname(&n2, nr->type); - cgen(nr, &n2); - nr = &n2; - } - if(!nl->addable) { - igen(nl, &n1, N); - nl = &n1; - } - - n3 = *n; - n3.left = nl; - n3.right = nr; - n3.op = n->etype; - - mgen(&n3, &n4, N); - gmove(&n4, nl); - - if(n1.op) - regfree(&n1); - mfree(&n2); - mfree(&n4); - -ret: - ; -} - -int -samereg(Node *a, Node *b) -{ - if(a->op != OREGISTER) - return 0; - if(b->op != OREGISTER) - return 0; - if(a->val.u.reg != b->val.u.reg) - return 0; - return 1; -} - -/* * generate division. * caller must set: * ax = allocated AX register diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c deleted file mode 100644 index b59d2268dd..0000000000 --- a/src/cmd/8g/gobj.c +++ /dev/null @@ -1,257 +0,0 @@ -// Derived from Inferno utils/8c/swt.c -// http://code.google.com/p/inferno-os/source/browse/utils/8c/swt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - -#include <u.h> -#include <libc.h> -#include "gg.h" - -int -dsname(Sym *s, int off, char *t, int n) -{ - Prog *p; - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = n; - p->from.sym = linksym(s); - - p->to.type = TYPE_SCONST; - memmove(p->to.u.sval, t, n); - return off + n; -} - -/* - * make a refer to the data s, s+len - * emitting DATA if needed. - */ -void -datastring(char *s, int len, Addr *a) -{ - Sym *sym; - - sym = stringsym(s, len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->sym = linksym(sym); - a->node = sym->def; - a->offset = widthptr+4; // skip header - a->etype = TINT32; -} - -/* - * make a refer to the string sval, - * emitting DATA if needed. - */ -void -datagostring(Strlit *sval, Addr *a) -{ - Sym *sym; - - sym = stringsym(sval->s, sval->len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->sym = linksym(sym); - a->node = sym->def; - a->offset = 0; // header - a->etype = TSTRING; -} - -void -gdata(Node *nam, Node *nr, int wid) -{ - Prog *p; - vlong v; - - if(nr->op == OLITERAL) { - switch(nr->val.ctype) { - case CTCPLX: - gdatacomplex(nam, nr->val.u.cval); - return; - case CTSTR: - gdatastring(nam, nr->val.u.sval); - return; - } - } - - if(wid == 8 && is64(nr->type)) { - v = mpgetfix(nr->val.u.xval); - p = gins(ADATA, nam, nodintconst(v)); - p->from3.type = TYPE_CONST; - p->from3.offset = 4; - p = gins(ADATA, nam, nodintconst(v>>32)); - p->from3.type = TYPE_CONST; - p->from3.offset = 4; - p->from.offset += 4; - return; - } - p = gins(ADATA, nam, nr); - p->from3.type = TYPE_CONST; - p->from3.offset = wid; -} - -void -gdatacomplex(Node *nam, Mpcplx *cval) -{ - Prog *p; - int w; - - w = cplxsubtype(nam->type->etype); - w = types[w]->width; - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->real); - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->from.offset += w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->imag); -} - -void -gdatastring(Node *nam, Strlit *sval) -{ - Prog *p; - Node nod1; - - p = gins(ADATA, nam, N); - datastring(sval->s, sval->len, &p->to); - p->from3.type = TYPE_CONST; - p->from3.offset = types[tptr]->width; - p->to.type = TYPE_ADDR; -//print("%P\n", p); - - nodconst(&nod1, types[TINT32], sval->len); - p = gins(ADATA, nam, &nod1); - p->from3.type = TYPE_CONST; - p->from3.offset = types[TINT32]->width; - p->from.offset += types[tptr]->width; -} - -int -dstringptr(Sym *s, int off, char *str) -{ - Prog *p; - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - - datastring(str, strlen(str)+1, &p->to); - p->to.type = TYPE_ADDR; - p->to.etype = TINT32; - off += widthptr; - - return off; -} - -int -dgostrlitptr(Sym *s, int off, Strlit *lit) -{ - Prog *p; - - if(lit == nil) - return duintptr(s, off, 0); - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - datagostring(lit, &p->to); - p->to.type = TYPE_ADDR; - p->to.etype = TINT32; - off += widthptr; - - return off; -} - -int -dgostringptr(Sym *s, int off, char *str) -{ - int n; - Strlit *lit; - - if(str == nil) - return duintptr(s, off, 0); - - n = strlen(str); - lit = mal(sizeof *lit + n); - strcpy(lit->s, str); - lit->len = n; - return dgostrlitptr(s, off, lit); -} - -int -dsymptr(Sym *s, int off, Sym *x, int xoff) -{ - Prog *p; - - off = rnd(off, widthptr); - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - p->to.type = TYPE_ADDR; - p->to.name = NAME_EXTERN; - p->to.sym = linksym(x); - p->to.offset = xoff; - off += widthptr; - - return off; -} - -void -nopout(Prog *p) -{ - p->as = ANOP; - p->from = zprog.from; - p->to = zprog.to; -} - diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index dd75e9f092..b82f7622ef 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -41,225 +41,6 @@ uint32 unmappedzero = 4096; #define CASE(a,b) (((a)<<16)|((b)<<0)) /*c2go int CASE(int, int);*/ -void -clearp(Prog *p) -{ - p->as = AEND; - p->from.type = TYPE_NONE; - p->from.index = TYPE_NONE; - p->to.type = TYPE_NONE; - p->to.index = TYPE_NONE; - p->pc = pcloc; - pcloc++; -} - -static int ddumped; -static Prog *dfirst; -static Prog *dpc; - -/* - * generate and return proc with p->as = as, - * linked into program. pc is next instruction. - */ -Prog* -prog(int as) -{ - Prog *p; - - if(as == ADATA || as == AGLOBL) { - if(ddumped) - fatal("already dumped data"); - if(dpc == nil) { - dpc = mal(sizeof(*dpc)); - dfirst = dpc; - } - p = dpc; - dpc = mal(sizeof(*dpc)); - p->link = dpc; - } else { - p = pc; - pc = mal(sizeof(*pc)); - clearp(pc); - p->link = pc; - } - - if(lineno == 0) { - if(debug['K']) - warn("prog: line 0"); - } - - p->as = as; - p->lineno = lineno; - return p; -} - -void -dumpdata(void) -{ - ddumped = 1; - if(dfirst == nil) - return; - newplist(); - *pc = *dfirst; - pc = dpc; - clearp(pc); -} - -/* - * 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, int likely) -{ - Prog *p; - - USED(t); - p = prog(as); - p->to.type = TYPE_BRANCH; - p->to.u.branch = P; - if(likely != 0) { - p->from.type = TYPE_CONST; - p->from.offset = likely > 0; - } - return p; -} - -/* - * patch previous branch to jump to to. - */ -void -patch(Prog *p, Prog *to) -{ - if(p->to.type != TYPE_BRANCH) - fatal("patch: not a branch"); - p->to.u.branch = to; - p->to.offset = to->pc; -} - -Prog* -unpatch(Prog *p) -{ - Prog *q; - - if(p->to.type != TYPE_BRANCH) - fatal("unpatch: not a branch"); - q = p->to.u.branch; - p->to.u.branch = P; - p->to.offset = 0; - return q; -} - -/* - * start a new Prog list. - */ -Plist* -newplist(void) -{ - Plist *pl; - - pl = linknewplist(ctxt); - - pc = mal(sizeof(*pc)); - clearp(pc); - pl->firstpc = pc; - - return pl; -} - -void -gused(Node *n) -{ - gins(ANOP, n, N); // used -} - -Prog* -gjmp(Prog *to) -{ - Prog *p; - - p = gbranch(AJMP, T, 0); - if(to != P) - patch(p, to); - return p; -} - -void -ggloblnod(Node *nam) -{ - Prog *p; - - p = gins(AGLOBL, nam, N); - p->lineno = nam->lineno; - p->from.sym->gotype = linksym(ngotype(nam)); - p->to.sym = nil; - p->to.type = TYPE_CONST; - p->to.offset = nam->type->width; - if(nam->readonly) - p->from3.offset = RODATA; - if(nam->type != T && !haspointers(nam->type)) - p->from3.offset |= NOPTR; -} - -void -ggloblsym(Sym *s, int32 width, int8 flags) -{ - Prog *p; - - p = gins(AGLOBL, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.index = REG_NONE; - p->from.sym = linksym(s); - p->to.type = TYPE_CONST; - p->to.index = REG_NONE; - p->to.offset = width; - p->from3.offset = flags; -} - -void -gtrack(Sym *s) -{ - Prog *p; - - p = gins(AUSEFIELD, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); -} - -int -isfat(Type *t) -{ - if(t != T) - switch(t->etype) { - case TSTRUCT: - case TARRAY: - case TSTRING: - case TINTER: // maybe remove later - return 1; - } - return 0; -} - -/* - * naddr of func generates code for address of func. - * if using opcode that can take address implicitly, - * call afunclit to fix up the argument. - */ -void -afunclit(Addr *a, Node *n) -{ - if(a->type == TYPE_ADDR && a->name == NAME_EXTERN) { - a->type = TYPE_MEM; - a->sym = linksym(n->sym); - } -} - /* * return Axxx for Oxxx on type t. */ @@ -406,6 +187,14 @@ optoas(int op, Type *t) case CASE(OAS, TPTR32): a = AMOVL; break; + + case CASE(OAS, TFLOAT32): + a = AMOVSS; + break; + + case CASE(OAS, TFLOAT64): + a = AMOVSD; + break; case CASE(OADD, TINT8): case CASE(OADD, TUINT8): @@ -878,7 +667,7 @@ gclean(void) yyerror("reg %R left allocated\n", i); } -int32 +int anyregalloc(void) { int i, j; @@ -935,9 +724,9 @@ regalloc(Node *n, Type *t, Node *o) if(reg[i] == 0) goto out; - fprint(2, "registers allocated at\n"); + print("registers allocated at\n"); for(i=REG_AX; i<=REG_DI; i++) - fprint(2, "\t%R\t%#lux\n", i, regpc[i]); + print("\t%R\t%#lux\n", i, regpc[i]); fatal("out of fixed registers"); goto err; @@ -955,9 +744,9 @@ regalloc(Node *n, Type *t, Node *o) for(i=REG_X0; i<=REG_X7; i++) if(reg[i] == 0) goto out; - fprint(2, "registers allocated at\n"); + print("registers allocated at\n"); for(i=REG_X0; i<=REG_X7; i++) - fprint(2, "\t%R\t%#lux\n", i, regpc[i]); + print("\t%R\t%#lux\n", i, regpc[i]); fatal("out of floating registers"); } yyerror("regalloc: unknown type %T", t); @@ -1002,105 +791,6 @@ regfree(Node *n) } /* - * initialize n to be register r of type t. - */ -void -nodreg(Node *n, Type *t, int r) -{ - if(t == T) - fatal("nodreg: t nil"); - - memset(n, 0, sizeof(*n)); - n->op = OREGISTER; - n->addable = 1; - ullmancalc(n); - n->val.u.reg = r; - n->type = t; -} - -/* - * initialize n to be indirect of register r; n is type t. - */ -void -nodindreg(Node *n, Type *t, int r) -{ - nodreg(n, t, r); - n->op = OINDREG; -} - -Node* -nodarg(Type *t, int fp) -{ - Node *n; - NodeList *l; - Type *first; - Iter savet; - - // entire argument struct, not just one arg - switch(t->etype) { - default: - fatal("nodarg %T", t); - - case TSTRUCT: - if(!t->funarg) - fatal("nodarg: TSTRUCT but not funarg"); - n = nod(ONAME, N, N); - n->sym = lookup(".args"); - n->type = t; - first = structfirst(&savet, &t); - if(first == nil) - fatal("nodarg: bad struct"); - if(first->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = first->width; - n->addable = 1; - break; - - case TFIELD: - if(fp == 1 && t->sym != S && !isblanksym(t->sym)) { - for(l=curfn->dcl; l; l=l->next) { - n = l->n; - if((n->class == PPARAM || n->class == PPARAMOUT) && n->sym == t->sym) - return n; - } - } - - n = nod(ONAME, N, N); - n->type = t->type; - n->sym = t->sym; - if(t->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = t->width; - n->addable = 1; - n->orig = t->nname; - break; - } - - // Rewrite argument named _ to __, - // or else the assignment to _ will be - // discarded during code generation. - if(isblank(n)) - n->sym = lookup("__"); - - switch(fp) { - default: - fatal("nodarg %T %d", t, fp); - - case 0: // output arg - n->op = OINDREG; - n->val.u.reg = REG_SP; - break; - - case 1: // input arg - n->class = PPARAM; - break; - } - - n->typecheck = 1; - return n; -} - -/* * generate * as $c, reg */ @@ -1142,26 +832,6 @@ ncon(uint32 i) return &n; } -/* - * Is this node a memory operand? - */ -int -ismem(Node *n) -{ - switch(n->op) { - case OITAB: - case OSPTR: - case OLEN: - case OCAP: - case OINDREG: - case ONAME: - case OPARAM: - case OCLOSUREVAR: - return 1; - } - return 0; -} - Node sclean[10]; int nsclean; @@ -1183,22 +853,25 @@ split64(Node *n, Node *lo, Node *hi) nsclean++; switch(n->op) { default: - if(!dotaddable(n, &n1)) { - igen(n, &n1, N); - sclean[nsclean-1] = n1; - } - n = &n1; - goto common; - case ONAME: - if(n->class == PPARAMREF) { - cgen(n->heapaddr, &n1); - sclean[nsclean-1] = n1; - // fall through. + switch(n->op) { + default: + if(!dotaddable(n, &n1)) { + igen(n, &n1, N); + sclean[nsclean-1] = n1; + } n = &n1; + break; + case ONAME: + if(n->class == PPARAMREF) { + cgen(n->heapaddr, &n1); + sclean[nsclean-1] = n1; + n = &n1; + } + break; + case OINDREG: + // nothing + break; } - goto common; - case OINDREG: - common: *lo = *n; *hi = *n; lo->type = types[TUINT32]; @@ -1408,8 +1081,8 @@ gmove(Node *f, Node *t) gins(AMOVL, &flo, &tlo); gins(AMOVL, &fhi, &thi); } else { - nodreg(&r1, t->type, REG_AX); - nodreg(&r2, t->type, REG_DX); + nodreg(&r1, types[TUINT32], REG_AX); + nodreg(&r2, types[TUINT32], REG_DX); gins(AMOVL, &flo, &r1); gins(AMOVL, &fhi, &r2); gins(AMOVL, &r1, &tlo); @@ -1796,7 +1469,7 @@ floatmove_387(Node *f, Node *t) gmove(f, &t1); switch(tt) { default: - fatal("gmove %T", t); + fatal("gmove %N", t); case TINT8: gins(ACMPL, &t1, ncon(-0x80)); p1 = gbranch(optoas(OLT, types[TINT32]), T, -1); @@ -2165,209 +1838,6 @@ gins(int as, Node *f, Node *t) return p; } -/* - * generate code to compute n; - * make a refer to result. - */ -void -naddr(Node *n, Addr *a, int canemitcode) -{ - Sym *s; - - a->scale = 0; - a->reg = REG_NONE; - a->index = REG_NONE; - a->type = TYPE_NONE; - a->name = NAME_NONE; - a->gotype = nil; - a->node = N; - if(n == N) - return; - - switch(n->op) { - default: - fatal("naddr: bad %O %D", n->op, a); - break; - - case OREGISTER: - a->type = TYPE_REG; - a->reg = n->val.u.reg; - a->sym = nil; - break; - - case OINDREG: - a->type = TYPE_MEM; - a->reg = n->val.u.reg; - a->sym = linksym(n->sym); - a->offset = n->xoffset; - break; - - case OPARAM: - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - a->etype = n->left->type->etype; - a->width = n->left->type->width; - a->offset = n->xoffset; - a->sym = linksym(n->left->sym); - a->type = TYPE_MEM; - a->name = NAME_PARAM; - a->node = n->left->orig; - break; - - case OCLOSUREVAR: - if(!curfn->needctxt) - fatal("closurevar without needctxt"); - a->type = TYPE_MEM; - a->reg = REG_DX; - a->offset = n->xoffset; - a->sym = nil; - break; - - case OCFUNC: - naddr(n->left, a, canemitcode); - a->sym = linksym(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->offset = n->xoffset; - s = n->sym; - a->node = n->orig; - //if(a->node >= (Node*)&n) - // fatal("stack node"); - if(s == S) - s = lookup(".noname"); - if(n->method) { - if(n->type != T) - if(n->type->sym != S) - if(n->type->sym->pkg != nil) - s = pkglookup(s->name, n->type->sym->pkg); - } - - switch(n->class) { - default: - fatal("naddr: ONAME class %S %d\n", n->sym, n->class); - case PEXTERN: - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - break; - case PAUTO: - a->type = TYPE_MEM; - a->name = NAME_AUTO; - break; - case PPARAM: - case PPARAMOUT: - a->type = TYPE_MEM; - a->name = NAME_PARAM; - break; - case PFUNC: - a->type = TYPE_ADDR; - a->name = NAME_EXTERN; - s = funcsym(s); - break; - } - a->sym = linksym(s); - break; - - case OLITERAL: - switch(n->val.ctype) { - default: - fatal("naddr: const %lT", n->type); - break; - case CTFLT: - a->type = TYPE_FCONST; - a->u.dval = mpgetflt(n->val.u.fval); - break; - case CTINT: - case CTRUNE: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = mpgetfix(n->val.u.xval); - break; - case CTSTR: - datagostring(n->val.u.sval, a); - break; - case CTBOOL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = n->val.u.bval; - break; - case CTNIL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = 0; - break; - } - break; - - case OADDR: - naddr(n->left, a, canemitcode); - if(a->type != TYPE_MEM) - fatal("naddr: OADDR %D", a); - a->type = TYPE_ADDR; - break; - - case OITAB: - // itable of interface value - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->etype = tptr; - a->width = widthptr; - break; - - case OSPTR: - // pointer in a string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // ptr(nil) - a->etype = simtype[tptr]; - a->offset += Array_array; - a->width = widthptr; - break; - - case OLEN: - // len of string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->etype = TUINT32; - a->offset += Array_nel; - a->width = 4; - break; - - case OCAP: - // cap of string or slice - naddr(n->left, a, canemitcode); - if(a->type == TYPE_CONST && a->offset == 0) - break; // cap(nil) - a->etype = TUINT32; - a->offset += Array_cap; - a->width = 4; - break; - -// case OADD: -// if(n->right->op == OLITERAL) { -// v = n->right->vconst; -// naddr(n->left, a, canemitcode); -// } else -// if(n->left->op == OLITERAL) { -// v = n->left->vconst; -// naddr(n->right, a, canemitcode); -// } else -// goto bad; -// a->offset += v; -// break; - - } -} - int dotaddable(Node *n, Node *n1) { @@ -2398,7 +1868,7 @@ sudoaddable(int as, Node *n, Addr *a) { USED(as); USED(n); - USED(a); + memset(a, 0, sizeof *a); return 0; } diff --git a/src/cmd/8g/opt.h b/src/cmd/8g/opt.h deleted file mode 100644 index 8378d5d456..0000000000 --- a/src/cmd/8g/opt.h +++ /dev/null @@ -1,192 +0,0 @@ -// Derived from Inferno utils/6c/gc.h -// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - - -#define Z N -#define Adr Addr - -#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]) -#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z]) - -#define CLOAD 5 -#define CREF 5 -#define CINF 1000 -#define LOOP 3 - -typedef struct Reg Reg; -typedef struct Rgn Rgn; - -/*c2go -extern Node *Z; -enum -{ - CLOAD = 5, - CREF = 5, - CINF = 1000, - LOOP = 3, -}; - -uint32 BLOAD(Reg*); -uint32 BSTORE(Reg*); -uint64 LOAD(Reg*); -uint64 STORE(Reg*); -*/ - -// 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->opt points back to r. -struct Reg -{ - Flow f; - - Bits set; // regopt variables written by this instruction. - Bits use1; // regopt variables read by prog->from. - Bits use2; // regopt variables read by prog->to. - - // refahead/refbehind are the regopt variables whose current - // value may be used in the following/preceding instructions - // up to a CALL (or the value is clobbered). - Bits refbehind; - Bits refahead; - // calahead/calbehind are similar, but for variables in - // instructions that are reachable after hitting at least one - // CALL. - Bits calbehind; - Bits calahead; - Bits regdiff; - Bits act; - - int32 regu; // register used bitmap - int32 rpo; // reverse post ordering - int32 active; - - uint16 loop; // x5 for every loop - uchar refset; // diagnostic generated - - Reg* p1; // predecessors of this instruction: p1, - Reg* p2; // and then p2 linked though p2link. - Reg* p2link; - Reg* s1; // successors of this instruction (at most two: s1 and s2). - Reg* s2; - Reg* link; // next instruction in function code - Prog* prog; // actual instruction -}; -#define R ((Reg*)0) -/*c2go extern Reg *R; */ - -#define NRGN 600 -/*c2go enum { NRGN = 600 }; */ - -// A Rgn represents a single regopt variable over a region of code -// where a register could potentially be dedicated to that variable. -// The code encompassed by a Rgn is defined by the flow graph, -// starting at enter, flood-filling forward while varno is refahead -// and backward while varno is refbehind, and following branches. A -// single variable may be represented by multiple disjoint Rgns and -// each Rgn may choose a different register for that variable. -// Registers are allocated to regions greedily in order of descending -// cost. -struct Rgn -{ - Reg* enter; - short cost; - short varno; - short regno; -}; - -EXTERN int32 exregoffset; // not set -EXTERN int32 exfregoffset; // not set -EXTERN Reg zreg; -EXTERN Reg* freer; -EXTERN Reg** rpo2r; -EXTERN Rgn region[NRGN]; -EXTERN Rgn* rgp; -EXTERN int nregion; -EXTERN int nvar; -EXTERN int32 regbits; -EXTERN int32 exregbits; -EXTERN Bits externs; -EXTERN Bits params; -EXTERN Bits consts; -EXTERN Bits addrs; -EXTERN Bits ivar; -EXTERN Bits ovar; -EXTERN int change; -EXTERN int32 maxnr; -EXTERN int32* idom; - -EXTERN struct -{ - int32 ncvtreg; - int32 nspill; - int32 nreload; - int32 ndelmov; - int32 nvar; - int32 naddr; -} ostats; - -/* - * reg.c - */ -Reg* rega(void); -int rcmp(const void*, const void*); -void regopt(Prog*); -void addmove(Reg*, int, int, int); -Bits mkvar(Reg*, Adr*); -void prop(Reg*, Bits, Bits); -void loopit(Reg*, int32); -void synch(Reg*, Bits); -uint32 allreg(uint32, Rgn*); -void paint1(Reg*, int); -uint32 paint2(Reg*, int, int); -void paint3(Reg*, int, uint32, int); -void addreg(Adr*, int); -void dumpone(Flow*, int); -void dumpit(char*, Flow*, int); - -/* - * peep.c - */ -void peep(Prog*); -void excise(Flow*); -int copyu(Prog*, Adr*, Adr*); - -uint32 RtoB(int); -uint32 FtoB(int); -int BtoR(uint32); -int BtoF(uint32); - -/* - * prog.c - */ -void proginfo(ProgInfo*, Prog*); diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index 6c0865a7e8..712c8fe11b 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -31,10 +31,11 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" enum { REGEXT = 0, + exregoffset = REG_DI, }; static void conprop(Flow *r); @@ -45,6 +46,7 @@ static int copy1(Adr*, Adr*, Flow*, int); static int copyas(Adr*, Adr*); static int copyau(Adr*, Adr*); static int copysub(Adr*, Adr*, Adr*, int); +static int copyu(Prog*, Adr*, Adr*); static uint32 gactive; @@ -92,7 +94,7 @@ peep(Prog *firstp) Prog *p, *p1; int t; - g = flowstart(firstp, sizeof(Flow)); + g = flowstart(firstp, 0); if(g == nil) return; gactive = 0; @@ -535,7 +537,7 @@ copy1(Adr *v1, Adr *v2, Flow *r, int f) * 4 if set and used * 0 otherwise (not touched) */ -int +static int copyu(Prog *p, Adr *v, Adr *s) { ProgInfo info; @@ -559,7 +561,7 @@ copyu(Prog *p, Adr *v, Adr *s) case ACALL: if(REGEXT && v->type == TYPE_REG && v->reg <= REGEXT && v->reg > exregoffset) return 2; - if(REGARG >= 0 && v->type == TYPE_REG && v->reg == (uchar)REGARG) + if(REGARG >= 0 && v->type == TYPE_REG && v->reg == REGARG) return 2; if(v->type == p->from.type && v->reg == p->from.reg) return 2; @@ -574,7 +576,7 @@ copyu(Prog *p, Adr *v, Adr *s) return 3; case ATEXT: - if(REGARG >= 0 && v->type == TYPE_REG && v->reg == (uchar)REGARG) + if(REGARG >= 0 && v->type == TYPE_REG && v->reg == REGARG) return 3; return 0; } diff --git a/src/cmd/8g/prog.c b/src/cmd/8g/prog.c index 8a7371b5c4..e77a026a93 100644 --- a/src/cmd/8g/prog.c +++ b/src/cmd/8g/prog.c @@ -5,7 +5,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" // Matches real RtoB but can be used in global initializer. #define RtoB(r) (1<<((r)-REG_AX)) diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 7d2de53549..0470bdf7b5 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -31,523 +31,34 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" -#define NREGVAR 16 /* 8 integer + 8 floating */ -#define REGBITS ((uint64)0xffffull) -/*c2go enum { - NREGVAR = 16, - REGBITS = (1<<NREGVAR) - 1, +enum { + NREGVAR = 16, /* 8 integer + 8 floating */ }; -*/ - -static Reg* firstr; -static int first = 1; - -int -rcmp(const void *a1, const void *a2) -{ - Rgn *p1, *p2; - int c1, c2; - - p1 = (Rgn*)a1; - p2 = (Rgn*)a2; - c1 = p2->cost; - c2 = p1->cost; - if(c1 -= c2) - return c1; - return p2->varno - p1->varno; -} - -static void -setaddrs(Bits bit) -{ - int i, n; - Var *v; - Node *node; - - while(bany(&bit)) { - // convert each bit to a variable - i = bnum(bit); - node = var[i].node; - n = var[i].name; - biclr(&bit, i); - - // disable all pieces of that variable - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node && v->name == n) - v->addr = 2; - } - } -} static char* regname[] = { ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di", ".x0", ".x1", ".x2", ".x3", ".x4", ".x5", ".x6", ".x7", }; -static Node* regnodes[NREGVAR]; - -static void walkvardef(Node *n, Reg *r, int active); - -void -regopt(Prog *firstp) +char** +regnames(int *n) { - Reg *r, *r1; - Prog *p; - Graph *g; - ProgInfo info; - int i, z, active; - uint32 vreg; - Bits bit; - - if(first) { - fmtinstall('Q', Qconv); - exregoffset = REG_DI; // no externals - first = 0; - } - - mergetemp(firstp); - - /* - * control flow is more complicated in generated go code - * than in generated c code. define pseudo-variables for - * registers, so we have complete register usage information. - */ - nvar = NREGVAR; - memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) { - if(regnodes[i] == N) - regnodes[i] = newname(lookup(regname[i])); - var[i].node = regnodes[i]; - } - - regbits = RtoB(REG_SP); - for(z=0; z<BITS; z++) { - externs.b[z] = 0; - params.b[z] = 0; - consts.b[z] = 0; - addrs.b[z] = 0; - ivar.b[z] = 0; - ovar.b[z] = 0; - } - - /* - * pass 1 - * build aux data structure - * allocate pcs - * find use and set of variables - */ - g = flowstart(firstp, sizeof(Reg)); - if(g == nil) { - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - return; - } - - firstr = (Reg*)g->start; - - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF || p->as == AVARKILL) - continue; - proginfo(&info, p); - - // Avoid making variables for direct-called functions. - if(p->as == ACALL && p->to.type == TYPE_MEM && p->to.name == NAME_EXTERN) - continue; - - r->use1.b[0] |= info.reguse | info.regindex; - r->set.b[0] |= info.regset; - - bit = mkvar(r, &p->from); - if(bany(&bit)) { - if(info.flags & LeftAddr) - setaddrs(bit); - if(info.flags & LeftRead) - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - if(info.flags & LeftWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - - bit = mkvar(r, &p->to); - if(bany(&bit)) { - if(info.flags & RightAddr) - setaddrs(bit); - if(info.flags & RightRead) - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - if(info.flags & RightWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - } - if(firstr == R) - return; - - for(i=0; i<nvar; i++) { - Var *v = var+i; - if(v->addr) { - bit = blsh(i); - for(z=0; z<BITS; z++) - addrs.b[z] |= bit.b[z]; - } - - 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->f, 1); - - /* - * pass 2 - * find looping structure - */ - flowrpo(g); - - if(debug['R'] && debug['v']) - dumpit("pass2", &firstr->f, 1); - - /* - * pass 2.5 - * iterate propagating fat vardef covering forward - * r->act records vars with a VARDEF since the last CALL. - * (r->act will be reused in pass 5 for something else, - * but we'll be done with it by then.) - */ - active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->f.active = 0; - r->act = zbits; - } - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF && isfat(((Node*)(p->to.node))->type) && ((Node*)(p->to.node))->opt != nil) { - active++; - walkvardef(p->to.node, r, active); - } - } - - /* - * pass 3 - * iterate propagating usage - * back until flow graph is complete - */ -loop1: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - if(r->f.prog->as == ARET) - prop(r, zbits, zbits); -loop11: - /* pick up unreachable code */ - i = 0; - for(r = firstr; r != R; r = r1) { - r1 = (Reg*)r->f.link; - if(r1 && r1->f.active && !r->f.active) { - prop(r, zbits, zbits); - i = 1; - } - } - if(i) - goto loop11; - if(change) - goto loop1; - - if(debug['R'] && debug['v']) - dumpit("pass3", &firstr->f, 1); - - /* - * pass 4 - * iterate propagating register/variable synchrony - * forward until graph is complete - */ -loop2: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - synch(firstr, zbits); - if(change) - goto loop2; - - if(debug['R'] && debug['v']) - dumpit("pass4", &firstr->f, 1); - - /* - * pass 4.5 - * move register pseudo-variables into regu. - */ - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; - - r->set.b[0] &= ~REGBITS; - r->use1.b[0] &= ~REGBITS; - r->use2.b[0] &= ~REGBITS; - r->refbehind.b[0] &= ~REGBITS; - r->refahead.b[0] &= ~REGBITS; - r->calbehind.b[0] &= ~REGBITS; - r->calahead.b[0] &= ~REGBITS; - r->regdiff.b[0] &= ~REGBITS; - r->act.b[0] &= ~REGBITS; - } - - /* - * pass 5 - * isolate regions - * calculate costs (paint1) - */ - r = firstr; - if(r) { - for(z=0; z<BITS; z++) - bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & - ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->f.refset) { - // should never happen - all variables are preset - if(debug['w']) - print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - } - } - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->act = zbits; - rgp = region; - nregion = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - for(z=0; z<BITS; z++) - bit.b[z] = r->set.b[z] & - ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->f.refset) { - if(debug['w']) - print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - excise(&r->f); - } - for(z=0; z<BITS; z++) - bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); - while(bany(&bit)) { - i = bnum(bit); - rgp->enter = r; - rgp->varno = i; - change = 0; - paint1(r, i); - biclr(&bit, i); - if(change <= 0) - continue; - rgp->cost = change; - nregion++; - if(nregion >= NRGN) { - if(debug['R'] && debug['v']) - print("too many regions\n"); - goto brk; - } - rgp++; - } - } -brk: - qsort(region, nregion, sizeof(region[0]), rcmp); - - /* - * pass 6 - * determine used registers (paint2) - * replace code (paint3) - */ - rgp = region; - if(debug['R'] && debug['v']) - print("\nregisterizing\n"); - for(i=0; i<nregion; i++) { - if(debug['R'] && debug['v']) - print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc); - bit = blsh(rgp->varno); - vreg = paint2(rgp->enter, rgp->varno, 0); - vreg = allreg(vreg, rgp); - if(rgp->regno != 0) - paint3(rgp->enter, rgp->varno, vreg, rgp->regno); - rgp++; - } - - /* - * free aux structures. peep allocates new ones. - */ - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - flowend(g); - firstr = R; - - if(debug['R'] && debug['v']) { - // Rebuild flow graph, since we inserted instructions - g = flowstart(firstp, sizeof(Reg)); - firstr = (Reg*)g->start; - dumpit("pass6", &firstr->f, 1); - flowend(g); - firstr = R; - } - - /* - * pass 7 - * peep-hole on basic block - */ - if(!debug['R'] || debug['P']) - peep(firstp); - - /* - * eliminate nops - */ - for(p=firstp; p!=P; p=p->link) { - while(p->link != P && p->link->as == ANOP) - p->link = p->link->link; - if(p->to.type == TYPE_BRANCH) - 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.reg >= REG_X0 && p->from.reg <= REG_X7) - fatal("invalid use of %R with GO386=387: %P", p->from.reg, p); - if(p->to.reg >= REG_X0 && p->to.reg <= REG_X7) - fatal("invalid use of %R with GO386=387: %P", p->to.reg, p); - } - - if(debug['R']) { - if(ostats.ncvtreg || - ostats.nspill || - ostats.nreload || - ostats.ndelmov || - ostats.nvar || - ostats.naddr || - 0) - print("\nstats\n"); - - if(ostats.ncvtreg) - print(" %4d cvtreg\n", ostats.ncvtreg); - if(ostats.nspill) - print(" %4d spill\n", ostats.nspill); - if(ostats.nreload) - print(" %4d reload\n", ostats.nreload); - if(ostats.ndelmov) - print(" %4d delmov\n", ostats.ndelmov); - if(ostats.nvar) - print(" %4d var\n", ostats.nvar); - if(ostats.naddr) - print(" %4d addr\n", ostats.naddr); - - memset(&ostats, 0, sizeof(ostats)); - } + *n = NREGVAR; + return regname; } -static void -walkvardef(Node *n, Reg *r, int active) +uint64 +excludedregs(void) { - Reg *r1, *r2; - int bn; - Var *v; - - for(r1=r; r1!=R; r1=(Reg*)r1->f.s1) { - if(r1->f.active == active) - break; - r1->f.active = active; - if(r1->f.prog->as == AVARKILL && r1->f.prog->to.node == n) - break; - for(v=n->opt; v!=nil; v=v->nextinnode) { - bn = v - var; - biset(&r1->act, bn); - } - if(r1->f.prog->as == ACALL) - break; - } - - for(r2=r; r2!=r1; r2=(Reg*)r2->f.s1) - if(r2->f.s2 != nil) - walkvardef(n, (Reg*)r2->f.s2, active); + return RtoB(REG_SP); } -/* - * add mov b,rn - * just after r - */ -void -addmove(Reg *r, int bn, int rn, int f) -{ - Prog *p, *p1; - Adr *a; - Var *v; - - p1 = mal(sizeof(*p1)); - clearp(p1); - p1->pc = 9999; - - p = r->f.prog; - p1->link = p->link; - p->link = p1; - p1->lineno = p->lineno; - - v = var + bn; - - a = &p1->to; - a->offset = v->offset; - a->etype = v->etype; - a->type = TYPE_MEM; - a->name = v->name; - a->node = v->node; - a->sym = linksym(v->node->sym); - - // need to clean this up with wptr and - // some of the defaults - p1->as = AMOVL; - switch(v->etype) { - default: - fatal("unknown type %E", v->etype); - case TINT8: - case TUINT8: - case TBOOL: - p1->as = AMOVB; - break; - case TINT16: - case TUINT16: - p1->as = AMOVW; - break; - case TFLOAT32: - p1->as = AMOVSS; - break; - case TFLOAT64: - p1->as = AMOVSD; - break; - case TINT: - case TUINT: - case TINT32: - case TUINT32: - case TPTR32: - break; - } - - p1->from.type = TYPE_REG; - p1->from.reg = rn; - p1->from.name = 0; - if(!f) { - p1->from = *a; - *a = zprog.from; - a->type = TYPE_REG; - a->reg = rn; - if(v->etype == TUINT8) - p1->as = AMOVB; - if(v->etype == TUINT16) - p1->as = AMOVW; - } - if(debug['R'] && debug['v']) - print("%P ===add=== %P\n", p, p1); - ostats.nspill++; -} - -uint32 +uint64 doregbits(int r) { - uint32 b; + uint64 b; b = 0; if(r >= REG_AX && r <= REG_DI) @@ -564,605 +75,17 @@ doregbits(int r) return b; } -static int -overlap(int32 o1, int w1, int32 o2, int w2) -{ - int32 t1, t2; - - t1 = o1+w1; - t2 = o2+w2; - - if(!(t1 > o2 && t2 > o1)) - return 0; - - return 1; -} - -Bits -mkvar(Reg *r, Adr *a) -{ - Var *v; - int i, n, et, z, w, flag, regu; - int32 o; - Bits bit; - Node *node; - - /* - * mark registers used - */ - if(a->type == TYPE_NONE) - goto none; - - if(r != R) - r->use1.b[0] |= doregbits(a->index); - - switch(a->type) { - default: - regu = doregbits(a->reg); - if(regu == 0) - goto none; - bit = zbits; - bit.b[0] = regu; - return bit; - - case TYPE_ADDR: - a->type = TYPE_MEM; - bit = mkvar(r, a); - setaddrs(bit); - a->type = TYPE_ADDR; - ostats.naddr++; - goto none; - - case TYPE_MEM: - switch(a->name) { - default: - goto none; - case NAME_EXTERN: - case NAME_STATIC: - case NAME_PARAM: - case NAME_AUTO: - n = a->name; - break; - } - } - - node = a->node; - if(node == N || node->op != ONAME || node->orig == N) - goto none; - node = node->orig; - if(node->orig != node) - fatal("%D: bad node", a); - if(node->sym == S || node->sym->name[0] == '.') - goto none; - 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++) { - v = var+i; - if(v->node == node && v->name == n) { - if(v->offset == o) - if(v->etype == et) - if(v->width == w) - return blsh(i); - - // if they overlap, disable both - if(overlap(v->offset, v->width, o, w)) { - if(debug['R']) - print("disable %s\n", node->sym->name); - v->addr = 1; - flag = 1; - } - } - } - - switch(et) { - case 0: - case TFUNC: - goto none; - } - - if(nvar >= NVAR) { - if(debug['w'] > 1 && node != N) - fatal("variable not optimized: %D", a); - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node) - v->addr = 1; - } - goto none; - } - - i = nvar; - nvar++; - v = var+i; - v->offset = o; - v->name = n; - v->etype = et; - v->width = w; - v->addr = flag; // funny punning - v->node = node; - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v->nextinnode = node->opt; - node->opt = v; - - bit = blsh(i); - if(n == NAME_EXTERN || n == NAME_STATIC) - for(z=0; z<BITS; z++) - externs.b[z] |= bit.b[z]; - if(n == NAME_PARAM) - for(z=0; z<BITS; z++) - params.b[z] |= bit.b[z]; - - if(node->class == PPARAM) - for(z=0; z<BITS; z++) - ivar.b[z] |= bit.b[z]; - if(node->class == PPARAMOUT) - for(z=0; z<BITS; z++) - ovar.b[z] |= bit.b[z]; - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in ../gc/plive.c does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.c:/^typecheckclosure and dcl.c:/^oldname. - if(node->addrtaken) - v->addr = 1; - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if(node->class == PEXTERN || (hasdefer && node->class == PPARAMOUT)) - v->addr = 1; - - if(debug['R']) - print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); - ostats.nvar++; - - return bit; - -none: - return zbits; -} - -void -prop(Reg *r, Bits ref, Bits cal) -{ - Reg *r1, *r2; - int z, i, j; - Var *v, *v1; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { - for(z=0; z<BITS; z++) { - ref.b[z] |= r1->refahead.b[z]; - if(ref.b[z] != r1->refahead.b[z]) { - r1->refahead.b[z] = ref.b[z]; - change++; - } - cal.b[z] |= r1->calahead.b[z]; - if(cal.b[z] != r1->calahead.b[z]) { - r1->calahead.b[z] = cal.b[z]; - change++; - } - } - switch(r1->f.prog->as) { - case ACALL: - if(noreturn(r1->f.prog)) - break; - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for(z=0; z<BITS; z++) { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1->act.b[z]; - ref.b[z] = 0; - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for(z=0; z<BITS; z++) { - if(cal.b[z] == 0) - continue; - for(i=0; i<64; i++) { - if(z*64+i >= nvar || ((cal.b[z]>>i)&1) == 0) - continue; - v = var+z*64+i; - if(v->node->opt == nil) // v represents fixed register, not Go variable - continue; - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1 = v->node->opt; - j = v1 - var; - if(v == v1 || !btest(&cal, j)) { - for(; v1 != nil; v1 = v1->nextinnode) { - j = v1 - var; - biset(&cal, j); - } - } - } - } - break; - - case ATEXT: - for(z=0; z<BITS; z++) { - cal.b[z] = 0; - ref.b[z] = 0; - } - break; - - case ARET: - for(z=0; z<BITS; z++) { - cal.b[z] = externs.b[z] | ovar.b[z]; - ref.b[z] = 0; - } - break; - } - for(z=0; z<BITS; z++) { - ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | - r1->use1.b[z] | r1->use2.b[z]; - cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); - r1->refbehind.b[z] = ref.b[z]; - r1->calbehind.b[z] = cal.b[z]; - } - if(r1->f.active) - break; - r1->f.active = 1; - } - for(; r != r1; r = (Reg*)r->f.p1) - for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) - prop(r2, r->refbehind, r->calbehind); -} - -void -synch(Reg *r, Bits dif) -{ - Reg *r1; - int z; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { - for(z=0; z<BITS; z++) { - dif.b[z] = (dif.b[z] & - ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | - r1->set.b[z] | r1->regdiff.b[z]; - if(dif.b[z] != r1->regdiff.b[z]) { - r1->regdiff.b[z] = dif.b[z]; - change++; - } - } - if(r1->f.active) - break; - r1->f.active = 1; - for(z=0; z<BITS; z++) - dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if((Reg*)r1->f.s2 != R) - synch((Reg*)r1->f.s2, dif); - } -} - -uint32 -allreg(uint32 b, Rgn *r) -{ - Var *v; - int i; - - v = var + r->varno; - r->regno = 0; - switch(v->etype) { - - default: - fatal("unknown etype %d/%E", bitno(b), v->etype); - break; - - case TINT8: - case TUINT8: - case TINT16: - case TUINT16: - case TINT32: - case TUINT32: - case TINT64: - case TINT: - case TUINT: - case TUINTPTR: - case TBOOL: - case TPTR32: - i = BtoR(~b); - if(i && r->cost > 0) { - r->regno = i; - return RtoB(i); - } - break; - - 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; -} - -void -paint1(Reg *r, int bn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb, rbz; - - z = bn/64; - bb = 1LL<<(bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - rbz = ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])); - if(LOAD(r) & rbz & bb) { - change -= CLOAD * r->f.loop; - } - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - if(r->f.prog->as != ANOP) { // don't give credit for NOPs - if(r->use1.b[z] & bb) { - change += CREF * r->f.loop; - if(p->as == AFMOVL || p->as == AFMOVW) - if(BtoR(bb) != REG_F0) - change = -CINF; - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - change += CREF * r->f.loop; - if(p->as == AFMOVL || p->as == AFMOVW) - if(BtoR(bb) != REG_F0) - change = -CINF; - } - } - - if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->f.loop; - if(p->as == AFMOVL || p->as == AFMOVW) - if(BtoR(bb) != REG_F0) - change = -CINF; - } - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint1(r1, bn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint1(r1, bn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -uint32 -paint2(Reg *r, int bn, int depth) -{ - Reg *r1; - int z; - uint64 bb, vreg; - - z = bn/64; - bb = 1LL << (bn%64); - vreg = regbits; - if(!(r->act.b[z] & bb)) - return vreg; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(!(r1->act.b[z] & bb)) - break; - r = r1; - } - for(;;) { - if(debug['R'] && debug['v']) - print(" paint2 %d %P\n", depth, r->f.prog); - - r->act.b[z] &= ~bb; - - vreg |= r->regu; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(!(r->act.b[z] & bb)) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } - - return vreg; -} - -void -paint3(Reg *r, int bn, uint32 rb, int rn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb, rbz; - - z = bn/64; - bb = 1LL << (bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - rbz = ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])); - if(LOAD(r) & rbz & bb) - addmove(r, bn, rn, 0); - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - if(r->use1.b[z] & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->from, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->to, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - - if(STORE(r) & r->regdiff.b[z] & bb) - addmove(r, bn, rn, 1); - r->regu |= rb; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint3(r1, bn, rb, rn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint3(r1, bn, rb, rn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -void -addreg(Adr *a, int rn) -{ - a->sym = nil; - a->node = nil; - a->offset = 0; - a->type = TYPE_REG; - a->reg = rn; - a->name = 0; - - ostats.ncvtreg++; -} - -uint32 +uint64 RtoB(int r) { if(r < REG_AX || r > REG_DI) return 0; - return 1L << (r-REG_AX); + return 1ULL << (r-REG_AX); } int -BtoR(uint32 b) +BtoR(uint64 b) { b &= 0xffL; @@ -1171,93 +94,19 @@ BtoR(uint32 b) return bitno(b) + REG_AX; } -uint32 +uint64 FtoB(int f) { if(f < REG_X0 || f > REG_X7) return 0; - return 1L << (f - REG_X0 + 8); + return 1ULL << (f - REG_X0 + 8); } int -BtoF(uint32 b) +BtoF(uint64 b) { b &= 0xFF00L; if(b == 0) return 0; return bitno(b) - 8 + REG_X0; } - -void -dumpone(Flow *f, int isreg) -{ - int z; - Bits bit; - Reg *r; - - print("%d:%P", f->loop, f->prog); - if(isreg) { - r = (Reg*)f; - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); - } - } - print("\n"); -} - -void -dumpit(char *str, Flow *r0, int isreg) -{ - Flow *r, *r1; - - print("\n%s\n", str); - for(r = r0; r != nil; r = r->link) { - dumpone(r, isreg); - r1 = r->p2; - if(r1 != nil) { - print(" pred:"); - for(; r1 != nil; r1 = r1->p2link) - print(" %.4ud", (int)r1->prog->pc); - print("\n"); - } - // Print successors if it's not just the next one - if(r->s1 != r->link || r->s2 != nil) { - print(" succ:"); - if(r->s1 != nil) - print(" %.4ud", (int)r->s1->prog->pc); - if(r->s2 != nil) - print(" %.4ud", (int)r->s2->prog->pc); - print("\n"); - } - } -} diff --git a/src/cmd/8l/8.out.h b/src/cmd/8l/8.out.h index 28d7dab7fb..273a8ad523 100644 --- a/src/cmd/8l/8.out.h +++ b/src/cmd/8l/8.out.h @@ -634,6 +634,7 @@ enum FREGRET = REG_F0, REGSP = REG_SP, REGTMP = REG_DI, + REGCTXT = REG_DX, }; /* diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 44d1ecc035..2da1651c8b 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -37,13 +37,6 @@ #include "../ld/macho.h" #include "../ld/pe.h" -char linuxdynld[] = "/lib/ld-linux.so.2"; -char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; -char openbsddynld[] = "/usr/libexec/ld.so"; -char netbsddynld[] = "/usr/libexec/ld.elf_so"; -char dragonflydynld[] = "/usr/libexec/ld-elf.so.2"; -char solarisdynld[] = "/lib/ld.so.1"; - static int needlib(char *name) { @@ -64,8 +57,6 @@ needlib(char *name) return 0; } -int nelfsym = 1; - static void addpltsym(Link*, LSym*); static void addgotsym(Link*, LSym*); @@ -141,7 +132,7 @@ adddynrel(LSym *s, Reloc *r) } addgotsym(ctxt, targ); r->type = R_CONST; // write r->add during relocsym - r->sym = S; + r->sym = nil; r->add += targ->got; return; @@ -218,7 +209,7 @@ adddynrel(LSym *s, Reloc *r) addaddrplus(ctxt, rel, s, r->off); adduint32(ctxt, rel, ELF32_R_INFO(targ->dynid, R_386_32)); r->type = R_CONST; // write r->add during relocsym - r->sym = S; + r->sym = nil; return; } if(HEADTYPE == Hdarwin && s->size == PtrSize && r->off == 0) { @@ -256,7 +247,7 @@ elfreloc1(Reloc *r, vlong sectoff) { int32 elfsym; - LPUT(sectoff); + thearch.lput(sectoff); elfsym = r->xsym->elfsym; switch(r->type) { @@ -265,7 +256,7 @@ elfreloc1(Reloc *r, vlong sectoff) case R_ADDR: if(r->siz == 4) - LPUT(R_386_32 | elfsym<<8); + thearch.lput(R_386_32 | elfsym<<8); else return -1; break; @@ -273,7 +264,7 @@ elfreloc1(Reloc *r, vlong sectoff) case R_CALL: case R_PCREL: if(r->siz == 4) - LPUT(R_386_PC32 | elfsym<<8); + thearch.lput(R_386_PC32 | elfsym<<8); else return -1; break; @@ -281,7 +272,7 @@ elfreloc1(Reloc *r, vlong sectoff) case R_TLS_LE: case R_TLS_IE: if(r->siz == 4) - LPUT(R_386_TLS_LE | elfsym<<8); + thearch.lput(R_386_TLS_LE | elfsym<<8); else return -1; } @@ -342,8 +333,8 @@ machoreloc1(Reloc *r, vlong sectoff) break; } - LPUT(sectoff); - LPUT(v); + thearch.lput(sectoff); + thearch.lput(v); return 0; } @@ -715,29 +706,3 @@ asmb(void) } cflush(); } - -void -s8put(char *n) -{ - char name[8]; - int i; - - strncpy(name, n, sizeof(name)); - for(i=0; i<sizeof(name); i++) - cput(name[i]); -} - -int32 -rnd(int32 v, int32 r) -{ - int32 c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 3c84d1b21f..e844bc4180 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -32,7 +32,6 @@ #include <libc.h> #include <bio.h> #include <link.h> -#include "8.out.h" #ifndef EXTERN #define EXTERN extern @@ -45,30 +44,10 @@ enum IntSize = 4, RegSize = 4, MaxAlign = 32, // max data alignment - FuncAlign = 16 -}; - -#define P ((Prog*)0) -#define S ((LSym*)0) - -enum -{ + FuncAlign = 16, MINLC = 1, }; -#pragma varargck type "I" uchar* - -EXTERN LSym* datap; -EXTERN int debug[128]; -EXTERN char literal[32]; -EXTERN Prog* firstp; -EXTERN int32 lcsize; -EXTERN char* rpath; -EXTERN int32 spsize; -EXTERN LSym* symlist; -EXTERN int32 symsize; - -int Iconv(Fmt *fp); void adddynlib(char *lib); void adddynrel(LSym *s, Reloc *r); void adddynrela(LSym *rela, LSym *s, Reloc *r); @@ -80,14 +59,6 @@ int elfreloc1(Reloc *r, vlong sectoff); void elfsetupplt(void); void listinit(void); int machoreloc1(Reloc *r, vlong sectoff); -int32 rnd(int32 v, int32 r); -void s8put(char *n); -char* xsymname(LSym *s); - -/* Native is little-endian */ -#define LPUT(a) lputl(a) -#define WPUT(a) wputl(a) -#define VPUT(a) vputl(a) /* Used by ../ld/dwarf.c */ enum diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 0a75340603..4cd8f08d34 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -37,31 +37,4 @@ void listinit(void) { listinit8(); - fmtinstall('I', Iconv); -} - -int -Iconv(Fmt *fp) -{ - int i, n; - uchar *p; - char *s; - Fmt fmt; - - n = fp->prec; - fp->prec = 0; - if(!(fp->flags&FmtPrec) || n < 0) - return fmtstrcpy(fp, "%I"); - fp->flags &= ~FmtPrec; - p = va_arg(fp->args, uchar*); - - // format into temporary buffer and - // call fmtstrcpy to handle padding. - fmtstrinit(&fmt); - for(i=0; i<n; i++) - fmtprint(&fmt, "%.2ux", *p++); - s = fmtstrflush(&fmt); - fmtstrcpy(fp, s); - free(s); - return 0; } diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 1b65c5eb9e..41900d6543 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -38,12 +38,50 @@ #include "../ld/pe.h" #include <ar.h> -char* thestring = "386"; -LinkArch* thelinkarch = &link386; +void +main(int argc, char **argv) +{ + linkarchinit(); + ldmain(argc, argv); +} void linkarchinit(void) { + thestring = "386"; + thelinkarch = &link386; + + thearch.thechar = thechar; + thearch.ptrsize = thelinkarch->ptrsize; + thearch.intsize = thelinkarch->ptrsize; + thearch.regsize = thelinkarch->regsize; + thearch.funcalign = FuncAlign; + thearch.maxalign = MaxAlign; + thearch.minlc = MINLC; + thearch.dwarfregsp = DWARFREGSP; + + thearch.adddynlib = adddynlib; + thearch.adddynrel = adddynrel; + thearch.adddynsym = adddynsym; + thearch.archinit = archinit; + thearch.archreloc = archreloc; + thearch.archrelocvariant = archrelocvariant; + thearch.asmb = asmb; + thearch.elfreloc1 = elfreloc1; + thearch.elfsetupplt = elfsetupplt; + thearch.gentext = gentext; + thearch.listinit = listinit; + thearch.machoreloc1 = machoreloc1; + thearch.lput = lputl; + thearch.wput = wputl; + thearch.vput = vputl; + + thearch.linuxdynld = "/lib/ld-linux.so.2"; + thearch.freebsddynld = "/usr/libexec/ld-elf.so.1"; + thearch.openbsddynld = "/usr/libexec/ld.so"; + thearch.netbsddynld = "/usr/libexec/ld.elf_so"; + thearch.dragonflydynld = "/usr/libexec/ld-elf.so.2"; + thearch.solarisdynld = "/lib/ld.so.1"; } void diff --git a/src/cmd/9g/cgen.c b/src/cmd/9g/cgen.c index 706658de0e..009ea1ed7a 100644 --- a/src/cmd/9g/cgen.c +++ b/src/cmd/9g/cgen.c @@ -805,7 +805,7 @@ ginsadd(int as, vlong off, Node *dst) void agen(Node *n, Node *res) { - Node *nl, *nr; + Node *nl; Node n1, n2, n3; if(debug['g']) { @@ -848,8 +848,6 @@ agen(Node *n, Node *res) } nl = n->left; - nr = n->right; - USED(nr); switch(n->op) { default: @@ -1109,27 +1107,20 @@ bgen(Node *n, int true, int likely, Prog *to) goto ret; case OANDAND: - if(!true) - goto caseor; - - caseand: - p1 = gbranch(ABR, T, 0); - p2 = gbranch(ABR, T, 0); - patch(p1, pc); - bgen(n->left, !true, -likely, p2); - bgen(n->right, !true, -likely, p2); - p1 = gbranch(ABR, T, 0); - patch(p1, to); - patch(p2, pc); - goto ret; - case OOROR: - if(!true) - goto caseand; - - caseor: - bgen(n->left, true, likely, to); - bgen(n->right, true, likely, to); + if((n->op == OANDAND) == true) { + p1 = gbranch(AJMP, T, 0); + p2 = gbranch(AJMP, T, 0); + patch(p1, pc); + bgen(n->left, !true, -likely, p2); + bgen(n->right, !true, -likely, p2); + p1 = gbranch(AJMP, T, 0); + patch(p1, to); + patch(p2, pc); + } else { + bgen(n->left, true, likely, to); + bgen(n->right, true, likely, to); + } goto ret; case OEQ: @@ -1554,7 +1545,7 @@ cadable(Node *n) int componentgen(Node *nr, Node *nl) { - Node nodl, nodr; + Node nodl, nodr, tmp; Type *t; int freel, freer; vlong fldcount; @@ -1602,7 +1593,7 @@ componentgen(Node *nr, Node *nl) nodl = *nl; if(!cadable(nl)) { - if(nr == N || !cadable(nr)) + if(nr != N && !cadable(nr)) goto no; igen(nl, &nodl, N); freel = 1; @@ -1614,6 +1605,12 @@ componentgen(Node *nr, Node *nl) 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; } // nl and nr are 'cadable' which basically means they are names (variables) now. @@ -1650,8 +1647,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1660,8 +1656,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1670,8 +1665,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_cap-Array_nel; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1685,8 +1679,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1695,8 +1688,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; @@ -1710,8 +1702,7 @@ componentgen(Node *nr, Node *nl) 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; @@ -1720,8 +1711,7 @@ componentgen(Node *nr, Node *nl) if(nr != N) { nodr.xoffset += Array_nel-Array_array; nodr.type = nodl.type; - } else - nodconst(&nodr, nodl.type, 0); + } gmove(&nodr, &nodl); goto yes; diff --git a/src/cmd/9g/galign.c b/src/cmd/9g/galign.c index df4291e90e..74856e2af0 100644 --- a/src/cmd/9g/galign.c +++ b/src/cmd/9g/galign.c @@ -14,12 +14,12 @@ void linkarchinit(void) { thestring = getgoarch(); - arch.thestring = thestring; + thearch.thestring = thestring; if(strcmp(thestring, "ppc64le") == 0) thelinkarch = &linkppc64le; else thelinkarch = &linkppc64; - arch.thelinkarch = thelinkarch; + thearch.thelinkarch = thelinkarch; } vlong MAXWIDTH = 1LL<<50; @@ -49,61 +49,46 @@ betypeinit(void) void main(int argc, char **argv) { - arch.thechar = thechar; - arch.thestring = thestring; - arch.thelinkarch = thelinkarch; - arch.typedefs = typedefs; - arch.MAXWIDTH = MAXWIDTH; - arch.afunclit = afunclit; - arch.anyregalloc = anyregalloc; - arch.betypeinit = betypeinit; - arch.bgen = bgen; - arch.cgen = cgen; - arch.cgen_asop = cgen_asop; - arch.cgen_call = cgen_call; - arch.cgen_callinter = cgen_callinter; - arch.cgen_ret = cgen_ret; - arch.clearfat = clearfat; - arch.clearp = clearp; - arch.defframe = defframe; - arch.dgostringptr = dgostringptr; - arch.dgostrlitptr = dgostrlitptr; - arch.dsname = dsname; - arch.dsymptr = dsymptr; - arch.dumpdata = dumpdata; - arch.dumpit = dumpit; - arch.excise = excise; - arch.expandchecks = expandchecks; - arch.fixautoused = fixautoused; - arch.gclean = gclean; - arch.gdata = gdata; - arch.gdatacomplex = gdatacomplex; - arch.gdatastring = gdatastring; - arch.ggloblnod = ggloblnod; - arch.ggloblsym = ggloblsym; - arch.ginit = ginit; - arch.gins = gins; - arch.ginscall = ginscall; - arch.gjmp = gjmp; - arch.gtrack = gtrack; - arch.gused = gused; - arch.igen = igen; - arch.isfat = isfat; - arch.linkarchinit = linkarchinit; - arch.markautoused = markautoused; - arch.naddr = naddr; - arch.newplist = newplist; - arch.nodarg = nodarg; - arch.patch = patch; - arch.proginfo = proginfo; - arch.regalloc = regalloc; - arch.regfree = regfree; - arch.regopt = regopt; - arch.regtyp = regtyp; - arch.sameaddr = sameaddr; - arch.smallindir = smallindir; - arch.stackaddr = stackaddr; - arch.unpatch = unpatch; + thearch.thechar = thechar; + thearch.thestring = thestring; + thearch.thelinkarch = thelinkarch; + thearch.typedefs = typedefs; + thearch.REGSP = REGSP; + thearch.REGCTXT = REGCTXT; + thearch.MAXWIDTH = MAXWIDTH; + thearch.anyregalloc = anyregalloc; + thearch.betypeinit = betypeinit; + thearch.bgen = bgen; + thearch.cgen = cgen; + thearch.cgen_call = cgen_call; + thearch.cgen_callinter = cgen_callinter; + thearch.cgen_ret = cgen_ret; + thearch.clearfat = clearfat; + thearch.defframe = defframe; + thearch.excise = excise; + thearch.expandchecks = expandchecks; + thearch.gclean = gclean; + thearch.ginit = ginit; + thearch.gins = gins; + thearch.ginscall = ginscall; + thearch.igen = igen; + thearch.linkarchinit = linkarchinit; + thearch.peep = peep; + thearch.proginfo = proginfo; + thearch.regalloc = regalloc; + thearch.regfree = regfree; + thearch.regtyp = regtyp; + thearch.sameaddr = sameaddr; + thearch.smallindir = smallindir; + thearch.stackaddr = stackaddr; + thearch.excludedregs = excludedregs; + thearch.RtoB = RtoB; + thearch.FtoB = RtoB; + thearch.BtoR = BtoR; + thearch.BtoF = BtoF; + thearch.optoas = optoas; + thearch.doregbits = doregbits; + thearch.regnames = regnames; gcmain(argc, argv); } diff --git a/src/cmd/9g/gg.h b/src/cmd/9g/gg.h index 367a858e86..938675937e 100644 --- a/src/cmd/9g/gg.h +++ b/src/cmd/9g/gg.h @@ -9,13 +9,7 @@ #include "../gc/go.h" #include "../9l/9.out.h" -// TODO(minux): Remove when no longer used. -#define noimpl sysfatal("%s not implemented (%s:%d).", __func__, __FILE__, __LINE__) - -EXTERN int32 dynloc; EXTERN uchar reg[NREG+NFREG]; -EXTERN int32 pcloc; // instruction counter -EXTERN Strlit emptystring; EXTERN Node* panicdiv; extern vlong unmappedzero; @@ -112,7 +106,6 @@ int anyregalloc(void); void betypeinit(void); void bgen(Node*, int, int, Prog*); void cgen(Node*, Node*); -void cgen_asop(Node*); void cgen_call(Node*, int); void cgen_callinter(Node*, Node*, int); void cgen_ret(Node*); @@ -158,3 +151,19 @@ int smallindir(Addr*, Addr*); int stackaddr(Addr*); Prog* unpatch(Prog*); + +/* + * reg.c + */ +uint64 excludedregs(void); +uint64 RtoB(int); +uint64 FtoB(int); +int BtoR(uint64); +int BtoF(uint64); +uint64 doregbits(int); +char** regnames(int*); + +/* + * peep.c + */ +void peep(Prog*); diff --git a/src/cmd/9g/ggen.c b/src/cmd/9g/ggen.c index 3865051018..7b34282685 100644 --- a/src/cmd/9g/ggen.c +++ b/src/cmd/9g/ggen.c @@ -7,7 +7,7 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" static Prog *appendpp(Prog *p, int as, int ftype, int freg, vlong foffset, int ttype, int treg, vlong toffset); static Prog *zerorange(Prog *p, vlong frame, vlong lo, vlong hi); @@ -113,51 +113,6 @@ appendpp(Prog *p, int as, int ftype, int freg, vlong foffset, int ttype, int tre return q; } -// Sweep the prog list to mark any used nodes. -void -markautoused(Prog *p) -{ - for (; p; p = p->link) { - if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL) - continue; - - if (p->from.node) - ((Node*)(p->from.node))->used = 1; - - if (p->to.node) - ((Node*)(p->to.node))->used = 1; - } -} - -// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. -void -fixautoused(Prog *p) -{ - Prog **lp; - - for (lp=&p; (p=*lp) != P; ) { - if (p->as == ATYPE && p->from.node && p->from.name == NAME_AUTO && !((Node*)(p->from.node))->used) { - *lp = p->link; - continue; - } - if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !((Node*)(p->to.node))->used) { - // Cannot remove VARDEF instruction, because - unlike TYPE handled above - - // VARDEFs are interspersed with other code, and a jump might be using the - // VARDEF as a target. Replace with a no-op instead. A later pass will remove - // the no-ops. - nopout(p); - continue; - } - if (p->from.name == NAME_AUTO && p->from.node) - p->from.offset += ((Node*)(p->from.node))->stkdelta; - - if (p->to.name == NAME_AUTO && p->to.node) - p->to.offset += ((Node*)(p->to.node))->stkdelta; - - lp = &p->link; - } -} - /* * generate: BL reg, f * where both reg and f are registers. @@ -226,7 +181,7 @@ ginscall(Node *f, int proc) gins(AUNDEF, N, N); break; } - nodreg(®, types[tptr], REGENV); + nodreg(®, types[tptr], REGCTXT); nodreg(&r1, types[tptr], REG_R3); gmove(f, ®); reg.op = OINDREG; @@ -487,27 +442,6 @@ cgen_ret(Node *n) } } -void -cgen_asop(Node *n) -{ - USED(n); - fatal("cgen_asop"); // no longer used -} - -int -samereg(Node *a, Node *b) -{ - if(a == N || b == N) - return 0; - if(a->op != OREGISTER) - return 0; - if(b->op != OREGISTER) - return 0; - if(a->val.u.reg != b->val.u.reg) - return 0; - return 1; -} - /* * generate division. * generates one of: diff --git a/src/cmd/9g/gobj.c b/src/cmd/9g/gobj.c deleted file mode 100644 index 35e4f458f1..0000000000 --- a/src/cmd/9g/gobj.c +++ /dev/null @@ -1,250 +0,0 @@ -// Derived from Inferno utils/6c/swt.c -// http://code.google.com/p/inferno-os/source/browse/utils/6c/swt.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - -#include <u.h> -#include <libc.h> -#include "gg.h" - -int -dsname(Sym *s, int off, char *t, int n) -{ - Prog *p; - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.offset = off; - p->from.sym = linksym(s); - - p->from3.type = TYPE_CONST; - p->from3.offset = n; - - p->to.type = TYPE_SCONST; - p->to.name = NAME_NONE; - p->to.offset = 0; - memmove(p->to.u.sval, t, n); - return off + n; -} - -/* - * make a refer to the data s, s+len - * emitting DATA if needed. - */ -void -datastring(char *s, int len, Addr *a) -{ - Sym *sym; - - sym = stringsym(s, len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->etype = simtype[TINT]; - a->offset = widthptr+widthint; // skip header - a->reg = 0; - a->sym = linksym(sym); - a->node = sym->def; -} - -/* - * make a refer to the string sval, - * emitting DATA if needed. - */ -void -datagostring(Strlit *sval, Addr *a) -{ - Sym *sym; - - sym = stringsym(sval->s, sval->len); - a->type = TYPE_MEM; - a->name = NAME_EXTERN; - a->sym = linksym(sym); - a->reg = 0; - a->node = sym->def; - a->offset = 0; // header - a->etype = TSTRING; -} - -void -gdata(Node *nam, Node *nr, int wid) -{ - Prog *p; - - if(nr->op == OLITERAL) { - switch(nr->val.ctype) { - case CTCPLX: - gdatacomplex(nam, nr->val.u.cval); - return; - case CTSTR: - gdatastring(nam, nr->val.u.sval); - return; - } - } - p = gins(ADATA, nam, nr); - p->from3.type = TYPE_CONST; - p->from3.offset = wid; -} - -void -gdatacomplex(Node *nam, Mpcplx *cval) -{ - Prog *p; - int w; - - w = cplxsubtype(nam->type->etype); - w = types[w]->width; - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->real); - - p = gins(ADATA, nam, N); - p->from3.type = TYPE_CONST; - p->from3.offset = w; - p->from.offset += w; - p->to.type = TYPE_FCONST; - p->to.u.dval = mpgetflt(&cval->imag); -} - -void -gdatastring(Node *nam, Strlit *sval) -{ - Prog *p; - Node nod1; - - p = gins(ADATA, nam, N); - datastring(sval->s, sval->len, &p->to); - p->from3.type = TYPE_CONST; - p->from3.offset = types[tptr]->width; - p->to.type = TYPE_ADDR; - p->to.etype = simtype[tptr]; - - nodconst(&nod1, types[TINT], sval->len); - p = gins(ADATA, nam, &nod1); - p->from3.type = TYPE_CONST; - p->from3.offset = widthint; - p->from.offset += widthptr; -} - -int -dstringptr(Sym *s, int off, char *str) -{ - Prog *p; - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - - datastring(str, strlen(str)+1, &p->to); - p->to.type = TYPE_CONST; - p->to.etype = simtype[TINT]; - off += widthptr; - - return off; -} - -int -dgostrlitptr(Sym *s, int off, Strlit *lit) -{ - Prog *p; - - if(lit == nil) - return duintptr(s, off, 0); - - off = rnd(off, widthptr); - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - datagostring(lit, &p->to); - p->to.type = TYPE_ADDR; - p->to.etype = simtype[TINT]; - off += widthptr; - - return off; -} - -int -dgostringptr(Sym *s, int off, char *str) -{ - int n; - Strlit *lit; - - if(str == nil) - return duintptr(s, off, 0); - - n = strlen(str); - lit = mal(sizeof *lit + n); - strcpy(lit->s, str); - lit->len = n; - return dgostrlitptr(s, off, lit); -} - -int -dsymptr(Sym *s, int off, Sym *x, int xoff) -{ - Prog *p; - - off = rnd(off, widthptr); - - p = gins(ADATA, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->from.offset = off; - p->from3.type = TYPE_CONST; - p->from3.offset = widthptr; - p->to.type = TYPE_ADDR; - p->to.name = NAME_EXTERN; - p->to.sym = linksym(x); - p->to.offset = xoff; - off += widthptr; - - return off; -} - -void -nopout(Prog *p) -{ - p->as = ANOP; - p->from = zprog.from; - p->from3 = zprog.from3; - p->reg = zprog.reg; - p->to = zprog.to; -} diff --git a/src/cmd/9g/gsubr.c b/src/cmd/9g/gsubr.c index 49f184d51e..1fb1511361 100644 --- a/src/cmd/9g/gsubr.c +++ b/src/cmd/9g/gsubr.c @@ -38,227 +38,6 @@ // At the same time, can raise StackBig in ../../runtime/stack.h. vlong unmappedzero = 4096; -void -clearp(Prog *p) -{ - *p = zprog; - p->as = AEND; - p->pc = pcloc; - pcloc++; -} - -static int ddumped; -static Prog *dfirst; -static Prog *dpc; - -/* - * generate and return proc with p->as = as, - * linked into program. pc is next instruction. - */ -Prog* -prog(int as) -{ - Prog *p; - - if(as == ADATA || as == AGLOBL) { - if(ddumped) - fatal("already dumped data"); - if(dpc == nil) { - dpc = mal(sizeof(*dpc)); - dfirst = dpc; - } - p = dpc; - dpc = mal(sizeof(*dpc)); - p->link = dpc; - p->reg = 0; // used for flags - } else { - p = pc; - pc = mal(sizeof(*pc)); - clearp(pc); - p->link = pc; - } - - if(lineno == 0) { - if(debug['K']) - warn("prog: line 0"); - } - - p->as = as; - p->lineno = lineno; - return p; -} - -void -dumpdata(void) -{ - ddumped = 1; - if(dfirst == nil) - return; - newplist(); - *pc = *dfirst; - pc = dpc; - clearp(pc); -} - -/* - * 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, int likely) -{ - Prog *p; - - USED(t); - - p = prog(as); - p->to.type = TYPE_BRANCH; - p->to.u.branch = P; - // TODO(minux): Enable this code. - // Note: liblink used Bcc CR0, label form, so we need another way - // to set likely/unlikely flag. Also note the y bit is not exactly - // likely/unlikely bit. - if(0 && as != ABR && likely != 0) { - p->from.type = TYPE_CONST; - p->from.offset = likely > 0; - } - return p; -} - -/* - * patch previous branch to jump to to. - */ -void -patch(Prog *p, Prog *to) -{ - if(p->to.type != TYPE_BRANCH) - fatal("patch: not a branch"); - p->to.u.branch = to; - p->to.offset = to->pc; -} - -Prog* -unpatch(Prog *p) -{ - Prog *q; - - if(p->to.type != TYPE_BRANCH) - fatal("unpatch: not a branch"); - q = p->to.u.branch; - p->to.u.branch = P; - p->to.offset = 0; - return q; -} - -/* - * start a new Prog list. - */ -Plist* -newplist(void) -{ - Plist *pl; - - pl = linknewplist(ctxt); - - pc = mal(sizeof(*pc)); - clearp(pc); - pl->firstpc = pc; - - return pl; -} - -void -gused(Node *n) -{ - gins(ANOP, n, N); // used -} - -Prog* -gjmp(Prog *to) -{ - Prog *p; - - p = gbranch(ABR, T, 0); - if(to != P) - patch(p, to); - return p; -} - -void -ggloblnod(Node *nam) -{ - Prog *p; - - p = gins(AGLOBL, nam, N); - p->lineno = nam->lineno; - p->from.sym->gotype = linksym(ngotype(nam)); - p->to.sym = nil; - p->to.type = TYPE_CONST; - p->to.offset = nam->type->width; - if(nam->readonly) - p->from3.offset = RODATA; - if(nam->type != T && !haspointers(nam->type)) - p->from3.offset |= NOPTR; -} - -void -gtrack(Sym *s) -{ - Prog *p; - - p = gins(AUSEFIELD, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); -} - -void -ggloblsym(Sym *s, int32 width, int8 flags) -{ - Prog *p; - - p = gins(AGLOBL, N, N); - p->from.type = TYPE_MEM; - p->from.name = NAME_EXTERN; - p->from.sym = linksym(s); - p->to.type = TYPE_CONST; - p->to.name = NAME_NONE; - p->to.offset = width; - p->from3.offset = flags; -} - -int -isfat(Type *t) -{ - if(t != T) - switch(t->etype) { - case TSTRUCT: - case TARRAY: - case TSTRING: - case TINTER: // maybe remove later - return 1; - } - return 0; -} - -/* - * naddr of func generates code for address of func. - * if using opcode that can take address implicitly, - * call afunclit to fix up the argument. - */ -void -afunclit(Addr *a, Node *n) -{ - if(a->type == TYPE_ADDR && a->name == NAME_EXTERN) { - a->type = TYPE_MEM; - a->sym = linksym(n->sym); - } -} - static int resvd[] = { REGZERO, @@ -308,7 +87,7 @@ gclean(void) yyerror("reg %R left allocated, %p\n", i+REG_R0, regpc[i]); } -int32 +int anyregalloc(void) { int i, j; @@ -432,109 +211,6 @@ regfree(Node *n) } /* - * initialize n to be register r of type t. - */ -void -nodreg(Node *n, Type *t, int r) -{ - if(t == T) - fatal("nodreg: t nil"); - - memset(n, 0, sizeof(*n)); - n->op = OREGISTER; - n->addable = 1; - ullmancalc(n); - n->val.u.reg = r; - n->type = t; -} - -/* - * initialize n to be indirect of register r; n is type t. - */ -void -nodindreg(Node *n, Type *t, int r) -{ - nodreg(n, t, r); - n->op = OINDREG; -} - -Node* -nodarg(Type *t, int fp) -{ - Node *n; - NodeList *l; - Type *first; - Iter savet; - - // entire argument struct, not just one arg - if(t->etype == TSTRUCT && t->funarg) { - n = nod(ONAME, N, N); - n->sym = lookup(".args"); - n->type = t; - first = structfirst(&savet, &t); - if(first == nil) - fatal("nodarg: bad struct"); - if(first->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = first->width; - n->addable = 1; - goto fp; - } - - if(t->etype != TFIELD) - fatal("nodarg: not field %T", t); - - if(fp == 1) { - for(l=curfn->dcl; l; l=l->next) { - n = l->n; - if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym) - return n; - } - } - - n = nod(ONAME, N, N); - n->type = t->type; - n->sym = t->sym; - - if(t->width == BADWIDTH) - fatal("nodarg: offset not computed for %T", t); - n->xoffset = t->width; - n->addable = 1; - n->orig = t->nname; - -fp: - // Rewrite argument named _ to __, - // or else the assignment to _ will be - // discarded during code generation. - if(isblank(n)) - n->sym = lookup("__"); - - switch(fp) { - default: - fatal("nodarg %T %d", t, fp); - - case 0: // output arg for calling another function - n->op = OINDREG; - n->val.u.reg = REGSP; - n->xoffset += 8; - break; - - case 1: // input arg to current function - n->class = PPARAM; - break; - - case 2: // offset output arg -fatal("shouldn't be used"); - n->op = OINDREG; - n->val.u.reg = REGSP; - n->xoffset += types[tptr]->width; - break; - } - n->typecheck = 1; - return n; -} - -/* * generate * as $c, n */ @@ -595,27 +271,6 @@ ginscon2(int as, Node *n2, vlong c) /*c2go int CASE(int, int); */ /* - * Is this node a memory operand? - */ -int -ismem(Node *n) -{ - switch(n->op) { - case OITAB: - case OSPTR: - case OLEN: - case OCAP: - case OINDREG: - case ONAME: - case OPARAM: - case OCLOSUREVAR: - case OADDR: - return 1; - } - return 0; -} - -/* * set up nodes representing 2^63 */ Node bigi; @@ -1077,8 +732,7 @@ fixlargeoffset(Node *n) if(n->xoffset != (int32)n->xoffset) { // TODO(minux): offset too large, move into R31 and add to R31 instead. // this is used only in test/fixedbugs/issue6036.go. - print("offset too large: %N\n", n); - noimpl; + fatal("offset too large: %N", n); a = *n; a.op = OREGISTER; a.type = types[tptr]; @@ -1090,208 +744,6 @@ fixlargeoffset(Node *n) } /* - * generate code to compute n; - * make a refer to result. - */ -void -naddr(Node *n, Addr *a, int canemitcode) -{ - Sym *s; - - a->type = TYPE_NONE; - a->name = NAME_NONE; - a->reg = 0; - a->gotype = nil; - a->node = N; - a->etype = 0; - 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); - break; - - case ONAME: - a->etype = 0; - a->reg = 0; - if(n->type != T) - a->etype = simtype[n->type->etype]; - a->offset = n->xoffset; - s = n->sym; - a->node = n->orig; - //if(a->node >= (Node*)&n) - // fatal("stack node"); - if(s == S) - s = lookup(".noname"); - if(n->method) { - if(n->type != T) - if(n->type->sym != S) - if(n->type->sym->pkg != nil) - s = pkglookup(s->name, n->type->sym->pkg); - } - - a->type = TYPE_MEM; - switch(n->class) { - default: - fatal("naddr: ONAME class %S %d\n", n->sym, n->class); - case PEXTERN: - a->name = NAME_EXTERN; - break; - case PAUTO: - a->name = NAME_AUTO; - break; - case PPARAM: - case PPARAMOUT: - a->name = NAME_PARAM; - break; - case PFUNC: - a->name = NAME_EXTERN; - a->type = TYPE_ADDR; - a->width = widthptr; - s = funcsym(s); - break; - } - a->sym = linksym(s); - break; - - case OLITERAL: - switch(n->val.ctype) { - default: - fatal("naddr: const %lT", n->type); - break; - case CTFLT: - a->type = TYPE_FCONST; - a->u.dval = mpgetflt(n->val.u.fval); - break; - case CTINT: - case CTRUNE: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = mpgetfix(n->val.u.xval); - break; - case CTSTR: - datagostring(n->val.u.sval, a); - break; - case CTBOOL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = n->val.u.bval; - break; - case CTNIL: - a->sym = nil; - a->type = TYPE_CONST; - a->offset = 0; - break; - } - break; - - case OREGISTER: - a->type = TYPE_REG; - a->reg = n->val.u.reg; - a->sym = nil; - break; - - case OINDREG: - a->type = TYPE_MEM; - a->reg = n->val.u.reg; - a->sym = linksym(n->sym); - a->offset = n->xoffset; - if(a->offset != (int32)a->offset) - yyerror("offset %lld too large for OINDREG", a->offset); - break; - - case OPARAM: - // n->left is PHEAP ONAME for stack parameter. - // compute address of actual parameter on stack. - a->etype = simtype[n->left->type->etype]; - a->width = n->left->type->width; - a->offset = n->xoffset; - a->sym = linksym(n->left->sym); - a->type = TYPE_MEM; - a->name = NAME_PARAM; - a->node = n->left->orig; - break; - - case OCLOSUREVAR: - if(!curfn->needctxt) - fatal("closurevar without needctxt"); - a->type = TYPE_MEM; - a->reg = REGENV; - a->offset = n->xoffset; - a->sym = nil; - break; - - case OCFUNC: - naddr(n->left, a, canemitcode); - a->sym = linksym(n->left->sym); - break; - - case OITAB: - // itable of interface value - naddr(n->left, a, canemitcode); - a->etype = simtype[tptr]; - if(a->type == TYPE_CONST && a->offset == 0) - break; // itab(nil) - a->width = widthptr; - break; - - case OSPTR: - // pointer in a string or slice - naddr(n->left, a, canemitcode); - a->etype = simtype[tptr]; - if(a->type == TYPE_CONST && a->offset == 0) - break; // ptr(nil) - a->offset += Array_array; - a->width = widthptr; - break; - - case OLEN: - // len of string or slice - naddr(n->left, a, canemitcode); - a->etype = simtype[TINT]; - if(a->type == TYPE_CONST && a->offset == 0) - break; // len(nil) - a->offset += Array_nel; - a->width = widthint; - break; - - case OCAP: - // cap of string or slice - naddr(n->left, a, canemitcode); - a->etype = simtype[TINT]; - if(a->type == TYPE_CONST && a->offset == 0) - break; // cap(nil) - a->offset += Array_cap; - a->width = widthint; - break; - - case OADDR: - naddr(n->left, a, canemitcode); - a->etype = tptr; - switch(a->type) { - case TYPE_MEM: - a->type = TYPE_ADDR; - break; - - case TYPE_REG: - case TYPE_CONST: - break; - - default: - fatal("naddr: OADDR %d\n", a->type); - } - break; - } -} - -/* * return Axxx for Oxxx on type t. */ int @@ -1700,6 +1152,7 @@ int sudoaddable(int as, Node *n, Addr *a) { // TODO(minux) - USED(as); USED(n); USED(a); + USED(as); USED(n); + memset(a, 0, sizeof *a); return 0; } diff --git a/src/cmd/9g/opt.h b/src/cmd/9g/opt.h index 25b6703279..79a34fb1f0 100644 --- a/src/cmd/9g/opt.h +++ b/src/cmd/9g/opt.h @@ -1,175 +1,6 @@ -// Derived from Inferno utils/6c/gc.h -// http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 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. - - -#define Z N -#define Adr Addr - -#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]) -#define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z]) - -#define CLOAD 5 -#define CREF 5 -#define CINF 1000 -#define LOOP 3 - -typedef struct Reg Reg; -typedef struct Rgn Rgn; - -/*c2go -extern Node *Z; -enum -{ - CLOAD = 5, - CREF = 5, - CINF = 1000, - LOOP = 3, -}; - -uint32 BLOAD(Reg*); -uint32 BSTORE(Reg*); -uint32 LOAD(Reg*); -uint32 STORE(Reg*); -*/ - -// 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->opt points back to r. -struct Reg -{ - Flow f; - - Bits set; // regopt variables written by this instruction. - Bits use1; // regopt variables read by prog->from. - Bits use2; // regopt variables read by prog->to. - - // refahead/refbehind are the regopt variables whose current - // value may be used in the following/preceding instructions - // up to a CALL (or the value is clobbered). - Bits refbehind; - Bits refahead; - // calahead/calbehind are similar, but for variables in - // instructions that are reachable after hitting at least one - // CALL. - Bits calbehind; - Bits calahead; - Bits regdiff; - Bits act; - - uint64 regu; // register used bitmap -}; -#define R ((Reg*)0) -/*c2go extern Reg *R; */ - -#define NRGN 600 -/*c2go enum { NRGN = 600 }; */ - -// A Rgn represents a single regopt variable over a region of code -// where a register could potentially be dedicated to that variable. -// The code encompassed by a Rgn is defined by the flow graph, -// starting at enter, flood-filling forward while varno is refahead -// and backward while varno is refbehind, and following branches. A -// single variable may be represented by multiple disjoint Rgns and -// each Rgn may choose a different register for that variable. -// Registers are allocated to regions greedily in order of descending -// cost. -struct Rgn -{ - Reg* enter; - short cost; - short varno; - short regno; -}; - -EXTERN int32 exregoffset; // not set -EXTERN int32 exfregoffset; // not set -EXTERN Reg zreg; -EXTERN Rgn region[NRGN]; -EXTERN Rgn* rgp; -EXTERN int nregion; -EXTERN int nvar; -EXTERN int32 regbits; -EXTERN int32 exregbits; // TODO(austin) not used; remove -EXTERN Bits externs; -EXTERN Bits params; -EXTERN Bits consts; -EXTERN Bits addrs; -EXTERN Bits ivar; -EXTERN Bits ovar; -EXTERN int change; -EXTERN int32 maxnr; - -EXTERN struct -{ - int32 ncvtreg; - int32 nspill; - int32 ndelmov; - int32 nvar; -} ostats; - -/* - * reg.c - */ -int rcmp(const void*, const void*); -void regopt(Prog*); -void addmove(Reg*, int, int, int); -Bits mkvar(Reg*, Adr*); -void prop(Reg*, Bits, Bits); -void synch(Reg*, Bits); -uint64 allreg(uint64, Rgn*); -void paint1(Reg*, int); -uint64 paint2(Reg*, int, int); -void paint3(Reg*, int, uint64, int); -void addreg(Adr*, int); -void dumpone(Flow*, int); -void dumpit(char*, Flow*, int); - -/* - * peep.c - */ -void peep(Prog*); -void excise(Flow*); -int copyu(Prog*, Adr*, Adr*); - -uint64 RtoB(int); -uint64 FtoB(int); -int BtoR(uint64); -int BtoF(uint64); - -/* - * prog.c - */ -void proginfo(ProgInfo*, Prog*); +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. // Many Power ISA arithmetic and logical instructions come in four // standard variants. These bits let us map between variants. diff --git a/src/cmd/9g/peep.c b/src/cmd/9g/peep.c index 95ff0b4d58..cf96d5dcda 100644 --- a/src/cmd/9g/peep.c +++ b/src/cmd/9g/peep.c @@ -31,6 +31,7 @@ #include <u.h> #include <libc.h> #include "gg.h" +#include "../gc/popt.h" #include "opt.h" static int regzer(Addr *a); @@ -42,6 +43,7 @@ static int copyau(Addr*, Addr*); static int copysub(Addr*, Addr*, Addr*, int); static int copysub1(Prog*, Addr*, Addr*, int); static int copyau1(Prog *p, Addr *v); +static int copyu(Prog *p, Addr *v, Addr *s); static uint32 gactive; @@ -53,7 +55,7 @@ peep(Prog *firstp) Prog *p, *p1; int t; - g = flowstart(firstp, sizeof(Flow)); + g = flowstart(firstp, 0); if(g == nil) return; gactive = 0; @@ -568,12 +570,12 @@ copy1(Addr *v1, Addr *v2, Flow *r, int f) // 4 if v is set in one address and used in another (so addresses // can be rewritten independently) // 0 otherwise (not touched) -int +static int copyu(Prog *p, Addr *v, Addr *s) { if(p->from3.type != TYPE_NONE) // 9g never generates a from3 - print("copyu: from3 (%D) not implemented\n", p->from3); + print("copyu: from3 (%D) not implemented\n", &p->from3); switch(p->as) { diff --git a/src/cmd/9g/prog.c b/src/cmd/9g/prog.c index 1775993a97..561249c358 100644 --- a/src/cmd/9g/prog.c +++ b/src/cmd/9g/prog.c @@ -5,6 +5,7 @@ #include <u.h> #include <libc.h> #include "gg.h" +#include "../gc/popt.h" #include "opt.h" enum { diff --git a/src/cmd/9g/reg.c b/src/cmd/9g/reg.c index a7ee07e547..84e1747e8d 100644 --- a/src/cmd/9g/reg.c +++ b/src/cmd/9g/reg.c @@ -31,56 +31,12 @@ #include <u.h> #include <libc.h> #include "gg.h" -#include "opt.h" +#include "../gc/popt.h" -#define NREGVAR 64 /* 32 general + 32 floating */ -#define REGBITS ((uint64)0xffffffffffffffffull) -/*c2go enum { - NREGVAR = 64, - REGBITS = 0xffffffffffffffff, +enum { + NREGVAR = 64, /* 32 general + 32 floating */ }; -*/ -static Reg* firstr; -static int first = 1; - -int -rcmp(const void *a1, const void *a2) -{ - Rgn *p1, *p2; - int c1, c2; - - p1 = (Rgn*)a1; - p2 = (Rgn*)a2; - c1 = p2->cost; - c2 = p1->cost; - if(c1 -= c2) - return c1; - return p2->varno - p1->varno; -} - -static void -setaddrs(Bits bit) -{ - int i, n; - Var *v; - Node *node; - - while(bany(&bit)) { - // convert each bit to a variable - i = bnum(bit); - node = var[i].node; - n = var[i].name; - biclr(&bit, i); - - // disable all pieces of that variable - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node && v->name == n) - v->addr = 2; - } - } -} static char* regname[] = { ".R0", @@ -149,1059 +105,32 @@ static char* regname[] = { ".F31", }; -static Node* regnodes[NREGVAR]; - -static void walkvardef(Node *n, Reg *r, int active); - -void -regopt(Prog *firstp) +char** +regnames(int *n) { - Reg *r, *r1; - Prog *p; - Graph *g; - ProgInfo info; - int i, z, active; - uint64 vreg, usedreg; - Bits bit; - - if(first) { - fmtinstall('Q', Qconv); - first = 0; - } - - mergetemp(firstp); + *n = NREGVAR; + return regname; +} - /* - * control flow is more complicated in generated go code - * than in generated c code. define pseudo-variables for - * registers, so we have complete register usage information. - */ - nvar = NREGVAR; - memset(var, 0, NREGVAR*sizeof var[0]); - for(i=0; i<NREGVAR; i++) { - if(regnodes[i] == N) - regnodes[i] = newname(lookup(regname[i])); - var[i].node = regnodes[i]; - } +uint64 +excludedregs(void) +{ + uint64 regbits; // Exclude registers with fixed functions regbits = (1<<0)|RtoB(REGSP)|RtoB(REGG)|RtoB(REGTLS); // Also exclude floating point registers with fixed constants regbits |= RtoB(REG_F27)|RtoB(REG_F28)|RtoB(REG_F29)|RtoB(REG_F30)|RtoB(REG_F31); - externs = zbits; - params = zbits; - consts = zbits; - addrs = zbits; - ivar = zbits; - ovar = zbits; - - /* - * pass 1 - * build aux data structure - * allocate pcs - * find use and set of variables - */ - g = flowstart(firstp, sizeof(Reg)); - if(g == nil) { - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - return; - } - - firstr = (Reg*)g->start; - - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF || p->as == AVARKILL) - continue; - proginfo(&info, p); - - // Avoid making variables for direct-called functions. - if(p->as == ABL && p->to.name == NAME_EXTERN) - continue; - - // from vs to doesn't matter for registers - r->use1.b[0] |= info.reguse | info.regindex; - r->set.b[0] |= info.regset; - - // Compute used register for from - bit = mkvar(r, &p->from); - if(info.flags & LeftAddr) - setaddrs(bit); - if(info.flags & LeftRead) - for(z=0; z<BITS; z++) - r->use1.b[z] |= bit.b[z]; - - // Compute used register for reg - if(info.flags & RegRead) - r->use1.b[0] |= RtoB(p->reg); - - // Currently we never generate three register forms. - // If we do, this will need to change. - if(p->from3.type != TYPE_NONE) - fatal("regopt not implemented for from3"); - - // Compute used register for to - bit = mkvar(r, &p->to); - if(info.flags & RightAddr) - setaddrs(bit); - if(info.flags & RightRead) - for(z=0; z<BITS; z++) - r->use2.b[z] |= bit.b[z]; - if(info.flags & RightWrite) - for(z=0; z<BITS; z++) - r->set.b[z] |= bit.b[z]; - } - - for(i=0; i<nvar; i++) { - Var *v = var+i; - if(v->addr) { - bit = blsh(i); - for(z=0; z<BITS; z++) - addrs.b[z] |= bit.b[z]; - } - - 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->f, 1); - - /* - * pass 2 - * find looping structure - */ - flowrpo(g); - - if(debug['R'] && debug['v']) - dumpit("pass2", &firstr->f, 1); - - /* - * pass 2.5 - * iterate propagating fat vardef covering forward - * r->act records vars with a VARDEF since the last CALL. - * (r->act will be reused in pass 5 for something else, - * but we'll be done with it by then.) - */ - active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->f.active = 0; - r->act = zbits; - } - for(r = firstr; r != R; r = (Reg*)r->f.link) { - p = r->f.prog; - if(p->as == AVARDEF && isfat(((Node*)(p->to.node))->type) && ((Node*)(p->to.node))->opt != nil) { - active++; - walkvardef(p->to.node, r, active); - } - } - - /* - * pass 3 - * iterate propagating usage - * back until flow graph is complete - */ -loop1: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - if(r->f.prog->as == ARET) - prop(r, zbits, zbits); -loop11: - /* pick up unreachable code */ - i = 0; - for(r = firstr; r != R; r = r1) { - r1 = (Reg*)r->f.link; - if(r1 && r1->f.active && !r->f.active) { - prop(r, zbits, zbits); - i = 1; - } - } - if(i) - goto loop11; - if(change) - goto loop1; - - if(debug['R'] && debug['v']) - dumpit("pass3", &firstr->f, 1); - - /* - * pass 4 - * iterate propagating register/variable synchrony - * forward until graph is complete - */ -loop2: - change = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->f.active = 0; - synch(firstr, zbits); - if(change) - goto loop2; - - if(debug['R'] && debug['v']) - dumpit("pass4", &firstr->f, 1); - - /* - * pass 4.5 - * move register pseudo-variables into regu. - */ - for(r = firstr; r != R; r = (Reg*)r->f.link) { - r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; - - r->set.b[0] &= ~REGBITS; - r->use1.b[0] &= ~REGBITS; - r->use2.b[0] &= ~REGBITS; - r->refbehind.b[0] &= ~REGBITS; - r->refahead.b[0] &= ~REGBITS; - r->calbehind.b[0] &= ~REGBITS; - r->calahead.b[0] &= ~REGBITS; - r->regdiff.b[0] &= ~REGBITS; - r->act.b[0] &= ~REGBITS; - } - - if(debug['R'] && debug['v']) - dumpit("pass4.5", &firstr->f, 1); - - /* - * pass 5 - * isolate regions - * calculate costs (paint1) - */ - r = firstr; - if(r) { - for(z=0; z<BITS; z++) - bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & - ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); - if(bany(&bit) && !r->f.refset) { - // should never happen - all variables are preset - if(debug['w']) - print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - } - } - for(r = firstr; r != R; r = (Reg*)r->f.link) - r->act = zbits; - rgp = region; - nregion = 0; - for(r = firstr; r != R; r = (Reg*)r->f.link) { - for(z=0; z<BITS; z++) - bit.b[z] = r->set.b[z] & - ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); - if(bany(&bit) && !r->f.refset) { - if(debug['w']) - print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); - r->f.refset = 1; - excise(&r->f); - } - for(z=0; z<BITS; z++) - bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); - while(bany(&bit)) { - i = bnum(bit); - rgp->enter = r; - rgp->varno = i; - change = 0; - paint1(r, i); - biclr(&bit, i); - if(change <= 0) - continue; - rgp->cost = change; - nregion++; - if(nregion >= NRGN) { - if(debug['R'] && debug['v']) - print("too many regions\n"); - goto brk; - } - rgp++; - } - } -brk: - qsort(region, nregion, sizeof(region[0]), rcmp); - - if(debug['R'] && debug['v']) - dumpit("pass5", &firstr->f, 1); - - /* - * pass 6 - * determine used registers (paint2) - * replace code (paint3) - */ - rgp = region; - if(debug['R'] && debug['v']) - print("\nregisterizing\n"); - for(i=0; i<nregion; i++) { - if(debug['R'] && debug['v']) - print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc); - bit = blsh(rgp->varno); - usedreg = paint2(rgp->enter, rgp->varno, 0); - vreg = allreg(usedreg, rgp); - if(rgp->regno != 0) { - if(debug['R'] && debug['v']) { - Var *v; - - v = var + rgp->varno; - print("registerize %N+%lld (bit=%2d et=%2E) in %R usedreg=%llx vreg=%llx\n", - v->node, v->offset, rgp->varno, v->etype, rgp->regno, usedreg, vreg); - } - paint3(rgp->enter, rgp->varno, vreg, rgp->regno); - } - rgp++; - } - - /* - * free aux structures. peep allocates new ones. - */ - for(i=0; i<nvar; i++) - var[i].node->opt = nil; - flowend(g); - firstr = R; - - if(debug['R'] && debug['v']) { - // Rebuild flow graph, since we inserted instructions - g = flowstart(firstp, sizeof(Reg)); - firstr = (Reg*)g->start; - dumpit("pass6", &firstr->f, 1); - flowend(g); - firstr = R; - } - - /* - * pass 7 - * peep-hole on basic block - */ - if(!debug['R'] || debug['P']) - peep(firstp); - - /* - * eliminate nops - */ - for(p=firstp; p!=P; p=p->link) { - while(p->link != P && p->link->as == ANOP) - p->link = p->link->link; - if(p->to.type == TYPE_BRANCH) - while(p->to.u.branch != P && p->to.u.branch->as == ANOP) - p->to.u.branch = p->to.u.branch->link; - } - - if(debug['R']) { - if(ostats.ncvtreg || - ostats.nspill || - ostats.ndelmov || - ostats.nvar || - 0) - print("\nstats\n"); - - if(ostats.ncvtreg) - print(" %4d cvtreg\n", ostats.ncvtreg); - if(ostats.nspill) - print(" %4d spill\n", ostats.nspill); - if(ostats.ndelmov) - print(" %4d delmov\n", ostats.ndelmov); - if(ostats.nvar) - print(" %4d var\n", ostats.nvar); - - memset(&ostats, 0, sizeof(ostats)); - } - - return; -} - -static void -walkvardef(Node *n, Reg *r, int active) -{ - Reg *r1, *r2; - int bn; - Var *v; - - for(r1=r; r1!=R; r1=(Reg*)r1->f.s1) { - if(r1->f.active == active) - break; - r1->f.active = active; - if(r1->f.prog->as == AVARKILL && r1->f.prog->to.node == n) - break; - for(v=n->opt; v!=nil; v=v->nextinnode) { - bn = v - var; - biset(&r1->act, bn); - } - if(r1->f.prog->as == ABL) - break; - } - - for(r2=r; r2!=r1; r2=(Reg*)r2->f.s1) - if(r2->f.s2 != nil) - walkvardef(n, (Reg*)r2->f.s2, active); -} - -/* - * add mov b,rn - * just after r - */ -void -addmove(Reg *r, int bn, int rn, int f) -{ - Prog *p, *p1, *p2; - Adr *a; - Var *v; - - p1 = mal(sizeof(*p1)); - *p1 = zprog; - p = r->f.prog; - - // If there's a stack fixup coming (ADD $n,R1 after BL newproc or BL deferproc), - // delay the load until after the fixup. - p2 = p->link; - if(p2 && p2->as == AADD && p2->to.reg == REGSP && p2->to.type == TYPE_REG) - p = p2; - - p1->link = p->link; - p->link = p1; - p1->lineno = p->lineno; - - v = var + bn; - - a = &p1->to; - a->name = v->name; - a->node = v->node; - a->sym = linksym(v->node->sym); - a->offset = v->offset; - a->etype = v->etype; - a->type = TYPE_MEM; - if(a->etype == TARRAY) - a->type = TYPE_ADDR; - else if(a->sym == nil) - a->type = TYPE_CONST; - - if(v->addr) - fatal("addmove: shouldn't be doing this %A\n", a); - - switch(v->etype) { - default: - print("What is this %E\n", v->etype); - - case TINT8: - p1->as = AMOVB; - break; - case TBOOL: - case TUINT8: -//print("movbu %E %d %S\n", v->etype, bn, v->sym); - p1->as = AMOVBZ; - break; - case TINT16: - p1->as = AMOVH; - break; - case TUINT16: - p1->as = AMOVHZ; - break; - case TINT32: - p1->as = AMOVW; - break; - case TUINT32: - case TPTR32: - p1->as = AMOVWZ; - break; - case TINT64: - case TUINT64: - case TPTR64: - p1->as = AMOVD; - break; - case TFLOAT32: - p1->as = AFMOVS; - break; - case TFLOAT64: - p1->as = AFMOVD; - break; - } - - p1->from.type = TYPE_REG; - p1->from.reg = rn; - if(!f) { - p1->from = *a; - *a = zprog.from; - a->type = TYPE_REG; - a->reg = rn; - if(v->etype == TUINT8 || v->etype == TBOOL) - p1->as = AMOVBZ; - if(v->etype == TUINT16) - p1->as = AMOVHZ; - } - if(debug['R']) - print("%P\t.a%P\n", p, p1); - ostats.nspill++; -} - -static int -overlap(int64 o1, int w1, int64 o2, int w2) -{ - int64 t1, t2; - - t1 = o1+w1; - t2 = o2+w2; - - if(!(t1 > o2 && t2 > o1)) - return 0; - - return 1; -} - -Bits -mkvar(Reg *r, Adr *a) -{ - USED(r); - Var *v; - int i, t, n, et, z, flag; - int64 w; - int64 o; - Bits bit; - Node *node; - - // mark registers used - t = a->type; - switch(t) { - default: - print("type %d %d %D\n", t, a->name, a); - goto none; - - case TYPE_NONE: - goto none; - - case TYPE_BRANCH: - case TYPE_CONST: - case TYPE_FCONST: - case TYPE_SCONST: - case TYPE_MEM: - case TYPE_ADDR: - break; - - case TYPE_REG: - if(a->reg != 0) { - bit = zbits; - bit.b[0] = RtoB(a->reg); - return bit; - } - break; - } - - switch(a->name) { - default: - goto none; - - case NAME_EXTERN: - case NAME_STATIC: - case NAME_AUTO: - case NAME_PARAM: - n = a->name; - break; - } - - node = a->node; - if(node == N || node->op != ONAME || node->orig == N) - goto none; - node = node->orig; - if(node->orig != node) - fatal("%D: bad node", a); - if(node->sym == S || node->sym->name[0] == '.') - goto none; - 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++) { - v = var+i; - if(v->node == node && v->name == n) { - if(v->offset == o) - if(v->etype == et) - if(v->width == w) - return blsh(i); - - // if they overlap, disable both - if(overlap(v->offset, v->width, o, w)) { - v->addr = 1; - flag = 1; - } - } - } - - switch(et) { - case 0: - case TFUNC: - goto none; - } - - if(nvar >= NVAR) { - if(debug['w'] > 1 && node != N) - fatal("variable not optimized: %#N", node); - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - for(i=0; i<nvar; i++) { - v = var+i; - if(v->node == node) - v->addr = 1; - } - goto none; - } - - i = nvar; - nvar++; - v = var+i; - v->offset = o; - v->name = n; - v->etype = et; - v->width = w; - v->addr = flag; // funny punning - v->node = node; - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v->nextinnode = node->opt; - node->opt = v; - - bit = blsh(i); - if(n == NAME_EXTERN || n == NAME_STATIC) - for(z=0; z<BITS; z++) - externs.b[z] |= bit.b[z]; - if(n == NAME_PARAM) - for(z=0; z<BITS; z++) - params.b[z] |= bit.b[z]; - - if(node->class == PPARAM) - for(z=0; z<BITS; z++) - ivar.b[z] |= bit.b[z]; - if(node->class == PPARAMOUT) - for(z=0; z<BITS; z++) - ovar.b[z] |= bit.b[z]; - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in ../gc/plive.c does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.c:/^typecheckclosure and dcl.c:/^oldname. - if(node->addrtaken) - v->addr = 1; - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if(node->class == PEXTERN || (hasdefer && node->class == PPARAMOUT)) - v->addr = 1; - - if(debug['R']) - print("bit=%2d et=%2E w=%lld+%lld %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); - ostats.nvar++; - - return bit; - -none: - return zbits; -} - -void -prop(Reg *r, Bits ref, Bits cal) -{ - Reg *r1, *r2; - int z, i, j; - Var *v, *v1; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.p1) { - for(z=0; z<BITS; z++) { - ref.b[z] |= r1->refahead.b[z]; - if(ref.b[z] != r1->refahead.b[z]) { - r1->refahead.b[z] = ref.b[z]; - change++; - } - cal.b[z] |= r1->calahead.b[z]; - if(cal.b[z] != r1->calahead.b[z]) { - r1->calahead.b[z] = cal.b[z]; - change++; - } - } - switch(r1->f.prog->as) { - case ABL: - if(noreturn(r1->f.prog)) - break; - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for(z=0; z<BITS; z++) { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1->act.b[z]; - ref.b[z] = 0; - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for(z=0; z<BITS; z++) { - if(cal.b[z] == 0) - continue; - for(i=0; i<64; i++) { - if(z*64+i >= nvar || ((cal.b[z]>>i)&1) == 0) - continue; - v = var+z*64+i; - if(v->node->opt == nil) // v represents fixed register, not Go variable - continue; - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1 = v->node->opt; - j = v1 - var; - if(v == v1 || !btest(&cal, j)) { - for(; v1 != nil; v1 = v1->nextinnode) { - j = v1 - var; - biset(&cal, j); - } - } - } - } - break; - - case ATEXT: - for(z=0; z<BITS; z++) { - cal.b[z] = 0; - ref.b[z] = 0; - } - break; - - case ARET: - for(z=0; z<BITS; z++) { - cal.b[z] = externs.b[z] | ovar.b[z]; - ref.b[z] = 0; - } - break; - } - for(z=0; z<BITS; z++) { - ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | - r1->use1.b[z] | r1->use2.b[z]; - cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); - r1->refbehind.b[z] = ref.b[z]; - r1->calbehind.b[z] = cal.b[z]; - } - if(r1->f.active) - break; - r1->f.active = 1; - } - for(; r != r1; r = (Reg*)r->f.p1) - for(r2 = (Reg*)r->f.p2; r2 != R; r2 = (Reg*)r2->f.p2link) - prop(r2, r->refbehind, r->calbehind); -} - -void -synch(Reg *r, Bits dif) -{ - Reg *r1; - int z; - - for(r1 = r; r1 != R; r1 = (Reg*)r1->f.s1) { - for(z=0; z<BITS; z++) { - dif.b[z] = (dif.b[z] & - ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | - r1->set.b[z] | r1->regdiff.b[z]; - if(dif.b[z] != r1->regdiff.b[z]) { - r1->regdiff.b[z] = dif.b[z]; - change++; - } - } - if(r1->f.active) - break; - r1->f.active = 1; - for(z=0; z<BITS; z++) - dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); - if(r1->f.s2 != nil) - synch((Reg*)r1->f.s2, dif); - } + return regbits; } uint64 -allreg(uint64 b, Rgn *r) +doregbits(int r) { - Var *v; - int i; - - v = var + r->varno; - r->regno = 0; - switch(v->etype) { - - default: - fatal("unknown etype %d/%E", bitno(b), v->etype); - break; - - 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 TPTR32: - case TPTR64: - i = BtoR(~b); - if(i && r->cost > 0) { - r->regno = i; - return RtoB(i); - } - break; - - case TFLOAT32: - case TFLOAT64: - i = BtoF(~b); - if(i && r->cost > 0) { - r->regno = i; - return RtoB(i); - } - break; - } + USED(r); return 0; } -void -paint1(Reg *r, int bn) -{ - Reg *r1; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL<<(bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { - change -= CLOAD * r->f.loop; - } - for(;;) { - r->act.b[z] |= bb; - - if(r->f.prog->as != ANOP) { // don't give credit for NOPs - if(r->use1.b[z] & bb) - change += CREF * r->f.loop; - if((r->use2.b[z]|r->set.b[z]) & bb) - change += CREF * r->f.loop; - } - - if(STORE(r) & r->regdiff.b[z] & bb) { - change -= CLOAD * r->f.loop; - } - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint1(r1, bn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint1(r1, bn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -uint64 -paint2(Reg *r, int bn, int depth) -{ - Reg *r1; - int z; - uint64 bb, vreg; - - z = bn/64; - bb = 1LL << (bn%64); - vreg = regbits; - if(!(r->act.b[z] & bb)) - return vreg; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(!(r1->act.b[z] & bb)) - break; - r = r1; - } - for(;;) { - if(debug['R'] && debug['v']) - print(" paint2 %d %P\n", depth, r->f.prog); - - r->act.b[z] &= ~bb; - - vreg |= r->regu; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - vreg |= paint2(r1, bn, depth+1); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(!(r->act.b[z] & bb)) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } - return vreg; -} - -void -paint3(Reg *r, int bn, uint64 rb, int rn) -{ - Reg *r1; - Prog *p; - int z; - uint64 bb; - - z = bn/64; - bb = 1LL << (bn%64); - if(r->act.b[z] & bb) - return; - for(;;) { - if(!(r->refbehind.b[z] & bb)) - break; - r1 = (Reg*)r->f.p1; - if(r1 == R) - break; - if(!(r1->refahead.b[z] & bb)) - break; - if(r1->act.b[z] & bb) - break; - r = r1; - } - - if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) - addmove(r, bn, rn, 0); - for(;;) { - r->act.b[z] |= bb; - p = r->f.prog; - - if(r->use1.b[z] & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->from, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - if((r->use2.b[z]|r->set.b[z]) & bb) { - if(debug['R'] && debug['v']) - print("%P", p); - addreg(&p->to, rn); - if(debug['R'] && debug['v']) - print(" ===change== %P\n", p); - } - - if(STORE(r) & r->regdiff.b[z] & bb) - addmove(r, bn, rn, 1); - r->regu |= rb; - - if(r->refbehind.b[z] & bb) - for(r1 = (Reg*)r->f.p2; r1 != R; r1 = (Reg*)r1->f.p2link) - if(r1->refahead.b[z] & bb) - paint3(r1, bn, rb, rn); - - if(!(r->refahead.b[z] & bb)) - break; - r1 = (Reg*)r->f.s2; - if(r1 != R) - if(r1->refbehind.b[z] & bb) - paint3(r1, bn, rb, rn); - r = (Reg*)r->f.s1; - if(r == R) - break; - if(r->act.b[z] & bb) - break; - if(!(r->refbehind.b[z] & bb)) - break; - } -} - -void -addreg(Adr *a, int rn) -{ - a->sym = nil; - a->node = nil; - a->name = NAME_NONE; - a->type = TYPE_REG; - a->reg = rn; - - ostats.ncvtreg++; -} - /* * track register variables including external registers: * bit reg @@ -1241,78 +170,3 @@ BtoF(uint64 b) return 0; return bitno(b) + REG_F0; } - -void -dumpone(Flow *f, int isreg) -{ - int z; - Bits bit; - Reg *r; - - print("%d:%P", f->loop, f->prog); - if(isreg) { - r = (Reg*)f; - for(z=0; z<BITS; z++) - bit.b[z] = - r->set.b[z] | - r->use1.b[z] | - r->use2.b[z] | - r->refbehind.b[z] | - r->refahead.b[z] | - r->calbehind.b[z] | - r->calahead.b[z] | - r->regdiff.b[z] | - r->act.b[z] | - 0; - if(bany(&bit)) { - print("\t"); - if(bany(&r->set)) - print(" s:%Q", r->set); - if(bany(&r->use1)) - print(" u1:%Q", r->use1); - if(bany(&r->use2)) - print(" u2:%Q", r->use2); - if(bany(&r->refbehind)) - print(" rb:%Q ", r->refbehind); - if(bany(&r->refahead)) - print(" ra:%Q ", r->refahead); - if(bany(&r->calbehind)) - print(" cb:%Q ", r->calbehind); - if(bany(&r->calahead)) - print(" ca:%Q ", r->calahead); - if(bany(&r->regdiff)) - print(" d:%Q ", r->regdiff); - if(bany(&r->act)) - print(" a:%Q ", r->act); - } - } - print("\n"); -} - - -void -dumpit(char *str, Flow *r0, int isreg) -{ - Flow *r, *r1; - - print("\n%s\n", str); - for(r = r0; r != nil; r = r->link) { - dumpone(r, isreg); - r1 = r->p2; - if(r1 != nil) { - print(" pred:"); - for(; r1 != nil; r1 = r1->p2link) - print(" %.4ud", (int)r1->prog->pc); - print("\n"); - } - // Print successors if it's not just the next one - if(r->s1 != r->link || r->s2 != nil) { - print(" succ:"); - if(r->s1 != nil) - print(" %.4ud", (int)r->s1->prog->pc); - if(r->s2 != nil) - print(" %.4ud", (int)r->s2->prog->pc); - print("\n"); - } - } -} diff --git a/src/cmd/9l/9.out.h b/src/cmd/9l/9.out.h index b99e7e6f4d..6e95698db9 100644 --- a/src/cmd/9l/9.out.h +++ b/src/cmd/9l/9.out.h @@ -204,7 +204,7 @@ enum REGRT1 = REG_R3, /* reserved for runtime, duffzero and duffcopy */ REGRT2 = REG_R4, /* reserved for runtime, duffcopy */ REGMIN = REG_R7, /* register variables allocated from here to REGMAX */ - REGENV = REG_R11, /* environment for closures */ + REGCTXT = REG_R11, /* context for closures */ REGTLS = REG_R13, /* C ABI TLS base pointer */ REGMAX = REG_R27, REGEXT = REG_R30, /* external registers allocated from here down */ diff --git a/src/cmd/9l/asm.c b/src/cmd/9l/asm.c index 391f9562cf..01e17aaa09 100644 --- a/src/cmd/9l/asm.c +++ b/src/cmd/9l/asm.c @@ -35,15 +35,6 @@ #include "../ld/elf.h" #include "../ld/dwarf.h" - -// TODO(austin): ABI v1 uses /usr/lib/ld.so.1 -char linuxdynld[] = "/lib64/ld64.so.1"; -char freebsddynld[] = "XXX"; -char openbsddynld[] = "XXX"; -char netbsddynld[] = "XXX"; -char dragonflydynld[] = "XXX"; -char solarisdynld[] = "XXX"; - static int needlib(char *name) { @@ -64,8 +55,6 @@ needlib(char *name) return 0; } -int nelfsym = 1; - static void gencallstub(int abicase, LSym *stub, LSym *targ); static void addpltsym(Link*, LSym*); static LSym* ensureglinkresolver(void); @@ -129,7 +118,7 @@ gentext(void) // This assumes "case 1" from the ABI, where the caller needs // us to save and restore the TOC pointer. pprevtextp = &ctxt->textp; - for(s=*pprevtextp; s!=S; pprevtextp=&s->next, s=*pprevtextp) { + for(s=*pprevtextp; s!=nil; pprevtextp=&s->next, s=*pprevtextp) { for(r=s->r; r<s->r+s->nr; r++) { if(!(r->type == 256 + R_PPC64_REL24 && r->sym->type == SDYNIMPORT)) @@ -797,14 +786,14 @@ asmb(void) switch(HEADTYPE) { default: case Hplan9: /* plan 9 */ - LPUT(0x647); /* magic */ - LPUT(segtext.filelen); /* sizes */ - LPUT(segdata.filelen); - LPUT(segdata.len - segdata.filelen); - LPUT(symsize); /* nsyms */ - LPUT(entryvalue()); /* va of entry */ - LPUT(0L); - LPUT(lcsize); + thearch.lput(0x647); /* magic */ + thearch.lput(segtext.filelen); /* sizes */ + thearch.lput(segdata.filelen); + thearch.lput(segdata.len - segdata.filelen); + thearch.lput(symsize); /* nsyms */ + thearch.lput(entryvalue()); /* va of entry */ + thearch.lput(0L); + thearch.lput(lcsize); break; case Hlinux: case Hfreebsd: @@ -824,18 +813,3 @@ asmb(void) print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); } } - -vlong -rnd(vlong v, int32 r) -{ - vlong c; - - if(r <= 0) - return v; - v += r - 1; - c = v % r; - if(c < 0) - c += r; - v -= c; - return v; -} diff --git a/src/cmd/9l/l.h b/src/cmd/9l/l.h index 9d8a4fae2c..a634cf80b4 100644 --- a/src/cmd/9l/l.h +++ b/src/cmd/9l/l.h @@ -44,55 +44,21 @@ enum IntSize = 8, RegSize = 8, MaxAlign = 32, // max data alignment - FuncAlign = 8 -}; - -#define P ((Prog*)0) -#define S ((LSym*)0) - -enum -{ - FPCHIP = 1, - STRINGSZ = 200, - MAXHIST = 20, /* limit of path elements for history symbols */ - DATBLK = 1024, - NHASH = 10007, - NHUNK = 100000, - MINSIZ = 64, - NENT = 100, - NSCHED = 20, + FuncAlign = 8, MINLC = 4, - - Roffset = 22, /* no. bits for offset in relocation address */ - Rindex = 10 /* no. bits for index in relocation address */ }; -EXTERN int32 autosize; -EXTERN LSym* datap; -EXTERN int debug[128]; -EXTERN int32 lcsize; -EXTERN char literal[32]; -EXTERN int nerrors; -EXTERN vlong instoffset; -EXTERN char* rpath; -EXTERN vlong pc; -EXTERN int32 symsize; -EXTERN int32 staticgen; -EXTERN Prog* lastp; -EXTERN vlong textsize; - -void asmb(void); void adddynlib(char *lib); void adddynrel(LSym *s, Reloc *r); +void adddynrela(LSym *rela, LSym *s, Reloc *r); void adddynsym(Link *ctxt, LSym *s); int archreloc(Reloc *r, LSym *s, vlong *val); vlong archrelocvariant(Reloc *r, LSym *s, vlong t); +void asmb(void); +int elfreloc1(Reloc *r, vlong sectoff); +void elfsetupplt(void); void listinit(void); -vlong rnd(vlong, int32); - -#define LPUT(a) (ctxt->arch->endian == BigEndian ? lputb(a):lputl(a)) -#define WPUT(a) (ctxt->arch->endian == BigEndian ? wputb(a):wputl(a)) -#define VPUT(a) (ctxt->arch->endian == BigEndian ? vputb(a):vputl(a)) +int machoreloc1(Reloc *r, vlong sectoff); /* Used by ../ld/dwarf.c */ enum diff --git a/src/cmd/9l/obj.c b/src/cmd/9l/obj.c index 77f665e5a2..17dd5caacd 100644 --- a/src/cmd/9l/obj.c +++ b/src/cmd/9l/obj.c @@ -36,8 +36,12 @@ #include "../ld/dwarf.h" #include <ar.h> -char *thestring = "ppc64"; -LinkArch *thelinkarch; +void +main(int argc, char **argv) +{ + linkarchinit(); + ldmain(argc, argv); +} void linkarchinit(void) @@ -47,6 +51,45 @@ linkarchinit(void) thelinkarch = &linkppc64le; else thelinkarch = &linkppc64; + + thearch.thechar = thechar; + thearch.ptrsize = thelinkarch->ptrsize; + thearch.intsize = thelinkarch->ptrsize; + thearch.regsize = thelinkarch->regsize; + thearch.funcalign = FuncAlign; + thearch.maxalign = MaxAlign; + thearch.minlc = MINLC; + thearch.dwarfregsp = DWARFREGSP; + + thearch.adddynlib = adddynlib; + thearch.adddynrel = adddynrel; + thearch.adddynsym = adddynsym; + thearch.archinit = archinit; + thearch.archreloc = archreloc; + thearch.archrelocvariant = archrelocvariant; + thearch.asmb = asmb; + thearch.elfreloc1 = elfreloc1; + thearch.elfsetupplt = elfsetupplt; + thearch.gentext = gentext; + thearch.listinit = listinit; + thearch.machoreloc1 = machoreloc1; + if(thelinkarch == &linkppc64le) { + thearch.lput = lputl; + thearch.wput = wputl; + thearch.vput = vputl; + } else { + thearch.lput = lputb; + thearch.wput = wputb; + thearch.vput = vputb; + } + + // TODO(austin): ABI v1 uses /usr/lib/ld.so.1 + thearch.linuxdynld = "/lib64/ld64.so.1"; + thearch.freebsddynld = "XXX"; + thearch.openbsddynld = "XXX"; + thearch.netbsddynld = "XXX"; + thearch.dragonflydynld = "XXX"; + thearch.solarisdynld = "XXX"; } void diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 1519d96ccc..85988e3bb7 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -107,22 +107,12 @@ func setContexts() { } } -var ( - internalPkg = regexp.MustCompile(`(^|/)internal($|/)`) - hashRx = regexp.MustCompile(`^[0-9a-f]{7,40}$`) -) - -func isDevelVersion(v string) bool { - if strings.Contains(v, "devel") { - return true - } - return hashRx.MatchString(v) -} +var internalPkg = regexp.MustCompile(`(^|/)internal($|/)`) func main() { flag.Parse() - if v := runtime.Version(); !strings.Contains(v, "weekly") && !isDevelVersion(v) { + 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 = "" @@ -293,7 +283,7 @@ func compareAPI(w io.Writer, features, required, optional, exception []string) ( delete(optionalSet, newFeature) } else { fmt.Fprintf(w, "+%s\n", newFeature) - if !*allowNew || !isDevelVersion(runtime.Version()) { + if !*allowNew || !strings.Contains(runtime.Version(), "devel") { ok = false // we're in lock-down mode for next release } } diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index a4bd5579d3..346ae94546 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -636,7 +636,7 @@ func (p *Package) writeExports(fgo2, fm io.Writer) { fgcc := creat(*objDir + "_cgo_export.c") fgcch := creat(*objDir + "_cgo_export.h") - fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") + fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n/* This file is arch-specific. */\n") fmt.Fprintf(fgcch, "%s\n", p.Preamble) fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog()) @@ -1310,6 +1310,10 @@ typedef double GoFloat64; typedef __complex float GoComplex64; typedef __complex double GoComplex128; +// static assertion to make sure the file is being used on architecture +// at least with matching size of GoInt. +typedef char _check_for_GOINTBITS_bit_pointer_matching_GoInt[sizeof(void*)==GOINTBITS/8 ? 1:-1]; + typedef struct { char *p; GoInt n; } GoString; typedef void *GoMap; typedef void *GoChan; diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 5cfc47ce84..81249d4645 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -32,7 +32,6 @@ var ( workdir string tooldir string gochar string - goversion string oldgoos string oldgoarch string oldgochar string @@ -225,8 +224,6 @@ func xinit() { os.Setenv("LANG", "C") os.Setenv("LANGUAGE", "en_US.UTF8") - goversion = findgoversion() - workdir = xworkdir() xatexit(rmworkdir) @@ -428,6 +425,7 @@ func setup() { } // For release, make sure excluded things are excluded. + goversion := findgoversion() if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) { for _, dir := range unreleased { if p := pathf("%s/%s", goroot, dir); isdir(p) { @@ -533,16 +531,16 @@ var deptab = []struct { "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libgc.a", }}, {"cmd/5l", []string{ - "../ld/*", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libld.a", }}, {"cmd/6l", []string{ - "../ld/*", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libld.a", }}, {"cmd/8l", []string{ - "../ld/*", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libld.a", }}, {"cmd/9l", []string{ - "../ld/*", + "$GOROOT/pkg/obj/${GOHOSTOS}_$GOHOSTARCH/libld.a", }}, {"cmd/go", []string{ "zdefaultcc.go", @@ -619,7 +617,7 @@ func install(dir string) { } // disable word wrapping in error messages gccargs = append(gccargs, "-fmessage-length=0") - if gohostos == "darwin" { + if gohostos == "darwin" && gohostarch != "arm" { // golang.org/issue/5261 gccargs = append(gccargs, "-mmacosx-version-min=10.6") } @@ -634,7 +632,7 @@ func install(dir string) { // Legacy C exceptions. switch dir { - case "lib9", "libbio", "liblink", "cmd/gc": + case "lib9", "libbio", "liblink", "cmd/gc", "cmd/ld": islib = true isgo = false case "cmd/5a", "cmd/5g", "cmd/5l", @@ -914,7 +912,7 @@ func install(dir string) { "-D", fmt.Sprintf("GOHOSTOS=%q", gohostos), "-D", fmt.Sprintf("GOHOSTARCH=%q", gohostarch), "-D", fmt.Sprintf("GOROOT=%q", goroot_final), - "-D", fmt.Sprintf("GOVERSION=%q", goversion), + "-D", fmt.Sprintf("GOVERSION=%q", findgoversion()), "-D", fmt.Sprintf("GOARM=%q", goarm), "-D", fmt.Sprintf("GO386=%q", go386), "-D", fmt.Sprintf("GO_EXTLINK_ENABLED=%q", goextlinkenabled), @@ -1117,6 +1115,7 @@ var buildorder = []string{ "liblink", "cmd/gc", // must be before g + "cmd/ld", // must be before l "cmd/%sl", // must be before a, g "cmd/%sa", "cmd/%sg", @@ -1505,5 +1504,5 @@ func cmdbanner() { // Version prints the Go version. func cmdversion() { xflagparse(0) - xprintf("%s\n", goversion) + xprintf("%s\n", findgoversion()) } diff --git a/src/cmd/dist/buildgc.go b/src/cmd/dist/buildgc.go index bc1898e04e..b59aa5b5a4 100644 --- a/src/cmd/dist/buildgc.go +++ b/src/cmd/dist/buildgc.go @@ -87,7 +87,7 @@ func mkanames(dir, file string) { line = line[:i] } line = line[2:] - fmt.Fprintf(&out, "\t\"%s\",\n", line) + fmt.Fprintf(&out, "\t\"%s\",\n", strings.TrimSpace(line)) } } fmt.Fprintf(&out, "};\n") diff --git a/src/cmd/dist/buildruntime.go b/src/cmd/dist/buildruntime.go index ff2a489b55..1f8276468e 100644 --- a/src/cmd/dist/buildruntime.go +++ b/src/cmd/dist/buildruntime.go @@ -28,7 +28,7 @@ func mkzversion(dir, file string) { "const defaultGoroot = `%s`\n"+ "const theVersion = `%s`\n"+ "const goexperiment = `%s`\n"+ - "var buildVersion = theVersion\n", goroot_final, goversion, os.Getenv("GOEXPERIMENT")) + "var buildVersion = theVersion\n", goroot_final, findgoversion(), os.Getenv("GOEXPERIMENT")) writefile(out, file, 0) } @@ -56,7 +56,7 @@ func mkzbootstrap(file string) { "const defaultGOARCH = `%s`\n"+ "const version = `%s`\n"+ "const goexperiment = `%s`\n", - goroot_final, goarm, gohostos, gohostarch, goversion, os.Getenv("GOEXPERIMENT")) + goroot_final, goarm, gohostos, gohostarch, findgoversion(), os.Getenv("GOEXPERIMENT")) writefile(out, file, 0) } diff --git a/src/cmd/dist/util.go b/src/cmd/dist/util.go index decbb0ffd2..df3e8154e2 100644 --- a/src/cmd/dist/util.go +++ b/src/cmd/dist/util.go @@ -414,7 +414,7 @@ func main() { if gohostarch == "" { // Default Unix system. - out := run("", CheckExit, "uname", "-m") + out := run("", CheckExit, "uname", "-m", "-v") switch { case strings.Contains(out, "x86_64"), strings.Contains(out, "amd64"): gohostarch = "amd64" @@ -426,6 +426,10 @@ func main() { gohostarch = "ppc64le" case strings.Contains(out, "ppc64"): gohostarch = "ppc64" + case gohostos == "darwin": + if strings.Contains(out, "RELEASE_ARM_") { + gohostarch = "arm" + } default: fatal("unknown architecture: %s", out) } @@ -491,6 +495,12 @@ func xgetgoarm() string { // NaCl guarantees VFPv3 and is always cross-compiled. return "7" } + if goos == "darwin" { + // Assume all darwin/arm devices are have VFPv3. This + // port is also mostly cross-compiled, so it makes little + // sense to auto-detect the setting. + return "7" + } if gohostarch != "arm" || goos != gohostos { // Conservative default for cross-compilation. return "5" @@ -499,24 +509,38 @@ func xgetgoarm() string { // FreeBSD has broken VFP support. return "5" } - if xtryexecfunc(useVFPv3) { + if goos != "linux" { + // All other arm platforms that we support + // require ARMv7. return "7" } - if xtryexecfunc(useVFPv1) { - return "6" - } - return "5" -} + cpuinfo := readfile("/proc/cpuinfo") + goarm := "5" + for _, line := range splitlines(cpuinfo) { + line := strings.SplitN(line, ":", 2) + if len(line) < 2 { + continue + } + if strings.TrimSpace(line[0]) != "Features" { + continue + } + features := splitfields(line[1]) + sort.Strings(features) // so vfpv3 sorts after vfp -func xtryexecfunc(f func()) bool { - // TODO(rsc): Implement. - // The C cmd/dist used this to test whether certain assembly - // sequences could be executed properly. It used signals and - // timers and sigsetjmp, which is basically not possible in Go. - // We probably have to invoke ourselves as a subprocess instead, - // to contain the fault/timeout. - return false + // Infer GOARM value from the vfp features available + // on this host. Values of GOARM detected are: + // 5: no vfp support was found + // 6: vfp (v1) support was detected, but no higher + // 7: vfpv3 support was detected. + // This matches the assertions in runtime.checkarm. + for _, f := range features { + switch f { + case "vfp": + goarm = "6" + case "vfpv3": + goarm = "7" + } + } + } + return goarm } - -func useVFPv1() -func useVFPv3() diff --git a/src/cmd/dist/vfp_arm.s b/src/cmd/dist/vfp_arm.s deleted file mode 100644 index 39052dbb30..0000000000 --- a/src/cmd/dist/vfp_arm.s +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "textflag.h" - -// try to run "vmov.f64 d0, d0" instruction -TEXT ·useVFPv1(SB),NOSPLIT,$0 - WORD $0xeeb00b40 // vmov.f64 d0, d0 - RET - -// try to run VFPv3-only "vmov.f64 d0, #112" instruction -TEXT ·useVFPv3(SB),NOSPLIT,$0 - WORD $0xeeb70b00 // vmov.f64 d0, #112 - RET diff --git a/src/cmd/dist/vfp_default.s b/src/cmd/dist/vfp_default.s deleted file mode 100644 index c795b357f7..0000000000 --- a/src/cmd/dist/vfp_default.s +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !arm - -#include "textflag.h" - -TEXT ·useVFPv1(SB),NOSPLIT,$0 - RET - -TEXT ·useVFPv3(SB),NOSPLIT,$0 - RET - diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 26d72e87fc..59ff0abf73 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -35,7 +35,7 @@ offmod(Type *t) fatal("offmod: not TFIELD: %lT", f); f->width = o; o += widthptr; - if(o >= arch.MAXWIDTH) { + if(o >= thearch.MAXWIDTH) { yyerror("interface too large"); o = widthptr; } @@ -86,7 +86,7 @@ widstruct(Type *errtype, Type *t, vlong o, int flag) if(w == 0) lastzero = o; o += w; - if(o >= arch.MAXWIDTH) { + if(o >= thearch.MAXWIDTH) { yyerror("type %lT too large", errtype); o = 8; // small but nonzero } @@ -260,7 +260,7 @@ dowidth(Type *t) dowidth(t->type); if(t->type->width != 0) { - cap = (arch.MAXWIDTH-1) / t->type->width; + cap = (thearch.MAXWIDTH-1) / t->type->width; if(t->bound > cap) yyerror("type %lT larger than address space", t); } @@ -613,15 +613,15 @@ typeinit(void) simtype[TFUNC] = tptr; simtype[TUNSAFEPTR] = tptr; - /* pick up the backend arch.typedefs */ - for(i=0; arch.typedefs[i].name; i++) { - s = lookup(arch.typedefs[i].name); - s1 = pkglookup(arch.typedefs[i].name, builtinpkg); + /* pick up the backend thearch.typedefs */ + for(i=0; thearch.typedefs[i].name; i++) { + s = lookup(thearch.typedefs[i].name); + s1 = pkglookup(thearch.typedefs[i].name, builtinpkg); - etype = arch.typedefs[i].etype; + etype = thearch.typedefs[i].etype; if(etype < 0 || etype >= nelem(types)) fatal("typeinit: %s bad etype", s->name); - sameas = arch.typedefs[i].sameas; + sameas = thearch.typedefs[i].sameas; if(sameas < 0 || sameas >= nelem(types)) fatal("typeinit: %s bad sameas", s->name); simtype[etype] = sameas; diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index f154ae70b1..1178f622e5 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -1,4 +1,7 @@ // AUTO-GENERATED by mkbuiltin; DO NOT EDIT +#include <u.h> +#include <libc.h> +#include "go.h" char *runtimeimport = "package runtime\n" "import runtime \"runtime\"\n" @@ -36,10 +39,10 @@ char *runtimeimport = "func @\"\".intstring (? *[4]byte, ? int64) (? string)\n" "func @\"\".slicebytetostring (? *[32]byte, ? []byte) (? string)\n" "func @\"\".slicebytetostringtmp (? []byte) (? string)\n" - "func @\"\".slicerunetostring (? []rune) (? string)\n" - "func @\"\".stringtoslicebyte (? string) (? []byte)\n" + "func @\"\".slicerunetostring (? *[32]byte, ? []rune) (? string)\n" + "func @\"\".stringtoslicebyte (? *[32]byte, ? string) (? []byte)\n" "func @\"\".stringtoslicebytetmp (? string) (? []byte)\n" - "func @\"\".stringtoslicerune (? string) (? []rune)\n" + "func @\"\".stringtoslicerune (? *[32]rune, ? string) (? []rune)\n" "func @\"\".stringiter (? string, ? int) (? int)\n" "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n" "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n" @@ -65,7 +68,7 @@ char *runtimeimport = "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 @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n" + "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64, @\"\".mapbuf·4 *any, @\"\".bucketbuf·5 *any) (@\"\".hmap·1 map[any]any)\n" "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index 5d25ffe4ad..0391ece379 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -105,6 +105,7 @@ typecheckclosure(Node *func, int top) oldfn = curfn; typecheck(&func->ntype, Etype); func->type = func->ntype->type; + func->top = top; // 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 @@ -119,9 +120,6 @@ typecheckclosure(Node *func, int top) curfn = oldfn; } - // Remember closure context for capturevars. - func->etype = (top & (Ecall|Eproc)) == Ecall; - // Create top-level function xtop = list(xtop, makeclosure(func)); } @@ -172,22 +170,17 @@ makeclosure(Node *func) // It decides whether each variable captured by a closure should be captured // by value or by reference. // We use value capturing for values <= 128 bytes that are never reassigned -// after declaration. +// after capturing (effectively constant). void capturevars(Node *xfunc) { - Node *func, *v, *addr, *cv, *outer; - NodeList *l, *body; - char *p; - vlong offset; - int nvar, lno; + Node *func, *v, *outer; + NodeList *l; + int lno; lno = lineno; lineno = xfunc->lineno; - nvar = 0; - body = nil; - offset = widthptr; func = xfunc->closure; func->enter = nil; for(l=func->cvars; l; l=l->next) { @@ -202,7 +195,6 @@ capturevars(Node *xfunc) v->op = OXXX; continue; } - nvar++; // type check the & of closed variables outside the closure, // so that the outer frame also grabs them and knows they escape. @@ -213,53 +205,150 @@ capturevars(Node *xfunc) if(outer->class != PPARAMOUT && !v->closure->addrtaken && !v->closure->assigned && v->type->width <= 128) v->byval = 1; else { + v->closure->addrtaken = 1; outer = nod(OADDR, outer, N); - // For a closure that is called in place, but not - // inside a go statement, avoid moving variables to the heap. - outer->etype = func->etype; } - if(debug['m'] > 1) + if(debug['m'] > 1) { + Sym *name; + char *how; + name = nil; + if(v->curfn && v->curfn->nname) + name = v->curfn->nname->sym; + how = "ref"; + if(v->byval) + how = "value"; warnl(v->lineno, "%S capturing by %s: %S (addr=%d assign=%d width=%d)", - (v->curfn && v->curfn->nname) ? v->curfn->nname->sym : S, v->byval ? "value" : "ref", + name, how, v->sym, v->closure->addrtaken, v->closure->assigned, (int32)v->type->width); + } typecheck(&outer, Erv); func->enter = list(func->enter, outer); + } + + lineno = lno; +} + +// transformclosure is called in a separate phase after escape analysis. +// It transform closure bodies to properly reference captured variables. +void +transformclosure(Node *xfunc) +{ + Node *func, *cv, *addr, *v, *f; + NodeList *l, *body; + Type **param, *fld; + vlong offset; + int lno, nvar; + + lno = lineno; + lineno = xfunc->lineno; + func = xfunc->closure; - // declare variables holding addresses taken from closure - // and initialize in entry prologue. - addr = nod(ONAME, N, N); - p = smprint("&%s", v->sym->name); - addr->sym = lookup(p); - free(p); - addr->ntype = nod(OIND, typenod(v->type), N); - addr->class = PAUTO; - addr->addable = 1; - addr->ullman = 1; - addr->used = 1; - addr->curfn = xfunc; - xfunc->dcl = list(xfunc->dcl, addr); - v->heapaddr = addr; - cv = nod(OCLOSUREVAR, N, N); - if(v->byval) { + if(func->top&Ecall) { + // If the closure is directly called, we transform it to a plain function call + // with variables passed as args. This avoids allocation of a closure object. + // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) + // will complete the transformation later. + // For illustration, the following closure: + // func(a int) { + // println(byval) + // byref++ + // }(42) + // becomes: + // func(a int, byval int, &byref *int) { + // println(byval) + // (*&byref)++ + // }(42, byval, &byref) + + // f is ONAME of the actual function. + f = xfunc->nname; + // Get pointer to input arguments and rewind to the end. + // We are going to append captured variables to input args. + param = &getinargx(f->type)->type; + for(; *param; param = &(*param)->down) { + } + for(l=func->cvars; l; l=l->next) { + v = l->n; + if(v->op == OXXX) + continue; + fld = typ(TFIELD); + fld->funarg = 1; + if(v->byval) { + // If v is captured by value, we merely downgrade it to PPARAM. + v->class = PPARAM; + v->ullman = 1; + fld->nname = v; + } else { + // If v of type T is captured by reference, + // we introduce function param &v *T + // and v remains PPARAMREF with &v heapaddr + // (accesses will implicitly deref &v). + snprint(namebuf, sizeof namebuf, "&%s", v->sym->name); + addr = newname(lookup(namebuf)); + addr->type = ptrto(v->type); + addr->class = PPARAM; + v->heapaddr = addr; + fld->nname = addr; + } + fld->type = fld->nname->type; + fld->sym = fld->nname->sym; + // Declare the new param and append it to input arguments. + xfunc->dcl = list(xfunc->dcl, fld->nname); + *param = fld; + param = &fld->down; + } + // Recalculate param offsets. + if(f->type->width > 0) + fatal("transformclosure: width is already calculated"); + dowidth(f->type); + xfunc->type = f->type; // update type of ODCLFUNC + } else { + // The closure is not called, so it is going to stay as closure. + nvar = 0; + body = nil; + offset = widthptr; + for(l=func->cvars; l; l=l->next) { + v = l->n; + if(v->op == OXXX) + continue; + nvar++; + // cv refers to the field inside of closure OSTRUCTLIT. + cv = nod(OCLOSUREVAR, N, N); cv->type = v->type; - offset = rnd(offset, v->type->align); + if(!v->byval) + cv->type = ptrto(v->type); + offset = rnd(offset, cv->type->align); cv->xoffset = offset; - offset += v->type->width; - body = list(body, nod(OAS, addr, nod(OADDR, cv, N))); - } else { - v->closure->addrtaken = 1; - cv->type = ptrto(v->type); - offset = rnd(offset, widthptr); - cv->xoffset = offset; - offset += widthptr; - body = list(body, nod(OAS, addr, cv)); + offset += cv->type->width; + + if(v->byval && v->type->width <= 2*widthptr && thearch.thechar == '6') { + // If it is a small variable captured by value, downgrade it to PAUTO. + // This optimization is currently enabled only for amd64, see: + // https://github.com/golang/go/issues/9865 + v->class = PAUTO; + v->ullman = 1; + xfunc->dcl = list(xfunc->dcl, v); + body = list(body, nod(OAS, v, cv)); + } else { + // Declare variable holding addresses taken from closure + // and initialize in entry prologue. + snprint(namebuf, sizeof namebuf, "&%s", v->sym->name); + addr = newname(lookup(namebuf)); + addr->ntype = nod(OIND, typenod(v->type), N); + addr->class = PAUTO; + addr->used = 1; + addr->curfn = xfunc; + xfunc->dcl = list(xfunc->dcl, addr); + v->heapaddr = addr; + if(v->byval) + cv = nod(OADDR, cv, N); + body = list(body, nod(OAS, addr, cv)); + } } + typechecklist(body, Etop); + walkstmtlist(body); + xfunc->enter = body; + xfunc->needctxt = nvar > 0; } - typechecklist(body, Etop); - walkstmtlist(body); - xfunc->enter = body; - xfunc->needctxt = nvar > 0; - func->etype = 0; lineno = lno; } diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index e418b9c561..50accb93a3 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -696,14 +696,7 @@ evconst(Node *n) // run op switch(TUP(n->op, v.ctype)) { default: - illegal: - if(!n->diag) { - yyerror("illegal constant expression: %T %O %T", - nl->type, n->op, nr->type); - n->diag = 1; - } - return; - + goto illegal; case TUP(OADD, CTINT): case TUP(OADD, CTRUNE): mpaddfixfix(v.u.xval, rv.u.xval, 0); @@ -1036,6 +1029,14 @@ setfalse: *n = *nodbool(0); n->orig = norig; return; + +illegal: + if(!n->diag) { + yyerror("illegal constant expression: %T %O %T", + nl->type, n->op, nr->type); + n->diag = 1; + } + return; } Node* @@ -1214,28 +1215,31 @@ defaultlit(Node **np, Type *t) case CTCPLX: t1 = types[TCOMPLEX128]; goto num; - num: - if(t != T) { - if(isint[t->etype]) { - t1 = t; - n->val = toint(n->val); - } - else - if(isfloat[t->etype]) { - t1 = t; - n->val = toflt(n->val); - } - else - if(iscomplex[t->etype]) { - t1 = t; - n->val = tocplx(n->val); - } + } + lineno = lno; + return; + +num: + if(t != T) { + if(isint[t->etype]) { + t1 = t; + n->val = toint(n->val); + } + else + if(isfloat[t->etype]) { + t1 = t; + n->val = toflt(n->val); + } + else + if(iscomplex[t->etype]) { + t1 = t; + n->val = tocplx(n->val); } - overflow(n->val, t1); - convlit(np, t1); - break; } + overflow(n->val, t1); + convlit(np, t1); lineno = lno; + return; } /* diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c index d3fb952558..26c21df82a 100644 --- a/src/cmd/gc/cplx.c +++ b/src/cmd/gc/cplx.c @@ -78,8 +78,8 @@ complexmove(Node *f, Node *t) subnode(&n1, &n2, f); subnode(&n3, &n4, t); - arch.cgen(&n1, &n3); - arch.cgen(&n2, &n4); + thearch.cgen(&n1, &n3); + thearch.cgen(&n2, &n4); break; } } @@ -151,9 +151,9 @@ complexgen(Node *n, Node *res) if(res->addable) { subnode(&n1, &n2, res); tempname(&tmp, n1.type); - arch.cgen(n->left, &tmp); - arch.cgen(n->right, &n2); - arch.cgen(&tmp, &n1); + thearch.cgen(n->left, &tmp); + thearch.cgen(n->right, &n2); + thearch.cgen(&tmp, &n1); return; } break; @@ -168,10 +168,10 @@ complexgen(Node *n, Node *res) } subnode(&n1, &n2, nl); if(n->op == OREAL) { - arch.cgen(&n1, res); + thearch.cgen(&n1, res); return; } - arch.cgen(&n2, res); + thearch.cgen(&n2, res); return; } @@ -191,9 +191,9 @@ complexgen(Node *n, Node *res) } if(!res->addable) { - arch.igen(res, &n1, N); - arch.cgen(n, &n1); - arch.regfree(&n1); + thearch.igen(res, &n1, N); + thearch.cgen(n, &n1); + thearch.regfree(&n1); return; } if(n->addable) { @@ -214,9 +214,9 @@ complexgen(Node *n, Node *res) case OCALLFUNC: case OCALLMETH: case OCALLINTER: - arch.igen(n, &n1, res); + thearch.igen(n, &n1, res); complexmove(&n1, res); - arch.regfree(&n1); + thearch.regfree(&n1); return; case OCONV: @@ -239,18 +239,18 @@ complexgen(Node *n, Node *res) if(nr != N) { if(nl->ullman > nr->ullman && !nl->addable) { tempname(&tnl, nl->type); - arch.cgen(nl, &tnl); + thearch.cgen(nl, &tnl); nl = &tnl; } if(!nr->addable) { tempname(&tnr, nr->type); - arch.cgen(nr, &tnr); + thearch.cgen(nr, &tnr); nr = &tnr; } } if(!nl->addable) { tempname(&tnl, nl->type); - arch.cgen(nl, &tnl); + thearch.cgen(nl, &tnl); nl = &tnl; } @@ -289,18 +289,18 @@ complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to) if(nr != N) { if(nl->ullman > nr->ullman && !nl->addable) { tempname(&tnl, nl->type); - arch.cgen(nl, &tnl); + thearch.cgen(nl, &tnl); nl = &tnl; } if(!nr->addable) { tempname(&tnr, nr->type); - arch.cgen(nr, &tnr); + thearch.cgen(nr, &tnr); nr = &tnr; } } if(!nl->addable) { tempname(&tnl, nl->type); - arch.cgen(nl, &tnl); + thearch.cgen(nl, &tnl); nl = &tnl; } @@ -331,7 +331,7 @@ complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to) if(op == ONE) true = !true; - arch.bgen(&na, true, likely, to); + thearch.bgen(&na, true, likely, to); } void @@ -387,7 +387,7 @@ minus(Node *nl, Node *res) ra.op = OMINUS; ra.left = nl; ra.type = nl->type; - arch.cgen(&ra, res); + thearch.cgen(&ra, res); } // build and execute tree @@ -424,14 +424,14 @@ complexadd(int op, Node *nl, Node *nr, Node *res) ra.left = &n1; ra.right = &n3; ra.type = n1.type; - arch.cgen(&ra, &n5); + thearch.cgen(&ra, &n5); memset(&ra, 0, sizeof(ra)); ra.op = op; ra.left = &n2; ra.right = &n4; ra.type = n2.type; - arch.cgen(&ra, &n6); + thearch.cgen(&ra, &n6); } // build and execute tree @@ -467,7 +467,7 @@ complexmul(Node *nl, Node *nr, Node *res) ra.left = &rm1; ra.right = &rm2; ra.type = rm1.type; - arch.cgen(&ra, &tmp); + thearch.cgen(&ra, &tmp); // imag part memset(&rm1, 0, sizeof(rm1)); @@ -487,8 +487,8 @@ complexmul(Node *nl, Node *nr, Node *res) ra.left = &rm1; ra.right = &rm2; ra.type = rm1.type; - arch.cgen(&ra, &n6); + thearch.cgen(&ra, &n6); // tmp ->real part - arch.cgen(&tmp, &n5); + thearch.cgen(&tmp, &n5); } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index f47ca2b50e..9a6c0023f5 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -1435,7 +1435,7 @@ addmethod(Sym *sf, Type *t, int local, int nointerface) } void -funccompile(Node *n, int isclosure) +funccompile(Node *n) { stksize = BADWIDTH; maxarg = 0; @@ -1449,20 +1449,6 @@ funccompile(Node *n, int isclosure) // assign parameter offsets checkwidth(n->type); - // record offset to actual frame pointer. - // for closure, have to skip over leading pointers and PC slot. - // TODO(rsc): this is the old jit closure handling code. - // with the new closures, isclosure is always 0; delete this block. - nodfp->xoffset = 0; - if(isclosure) { - NodeList *l; - for(l=n->nname->ntype->list; l; l=l->next) { - nodfp->xoffset += widthptr; - if(l->n->left == N) // found slot for PC - break; - } - } - if(curfn) fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym); diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 4f77983926..5b09c0b7fb 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -693,12 +693,10 @@ esc(EscState *e, Node *n, Node *up) case OMAKEMAP: case OMAKESLICE: case ONEW: - n->escloopdepth = e->loopdepth; - n->esc = EscNone; // until proven otherwise - e->noesc = list(e->noesc, n); - break; - + case OARRAYRUNESTR: case OARRAYBYTESTR: + case OSTRARRAYRUNE: + case OSTRARRAYBYTE: case ORUNESTR: n->escloopdepth = e->loopdepth; n->esc = EscNone; // until proven otherwise @@ -824,7 +822,10 @@ escassign(EscState *e, Node *dst, Node *src) case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: + case OARRAYRUNESTR: case OARRAYBYTESTR: + case OSTRARRAYRUNE: + case OSTRARRAYBYTE: case OADDSTR: case ONEW: case OCLOSURE: @@ -1249,7 +1250,10 @@ escwalk(EscState *e, int level, Node *dst, Node *src) case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: + case OARRAYRUNESTR: case OARRAYBYTESTR: + case OSTRARRAYRUNE: + case OSTRARRAYBYTE: case OADDSTR: case OMAPLIT: case ONEW: diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 8685aa0def..91f4957dc2 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -538,7 +538,7 @@ dumpasmhdr(void) b = Bopen(asmhdr, OWRITE); if(b == nil) fatal("open %s: %r", asmhdr); - Bprint(b, "// generated by %cg -asmhdr from package %s\n\n", arch.thechar, localpkg->name); + Bprint(b, "// generated by %cg -asmhdr from package %s\n\n", thearch.thechar, localpkg->name); for(l=asmlist; l; l=l->next) { n = l->n; if(isblanksym(n->sym)) diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index f9eb0ba2b3..bd0a29f6e1 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -171,7 +171,7 @@ goopnames[] = }; // Fmt "%O": Node opcodes -static int +int Oconv(Fmt *fp) { int o; @@ -1558,7 +1558,7 @@ Sconv(Fmt *fp) // Flags: 'l' print definition, not name // 'h' omit 'func' and receiver from function types, short type names // 'u' package name, not prefix (FTypeId mode, sticky) -static int +int Tconv(Fmt *fp) { Type *t; diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 20c17bd46a..72bcc492a5 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -265,7 +265,7 @@ gen(Node *n) //dump("gen", n); lno = setlineno(n); - wasregalloc = arch.anyregalloc(); + wasregalloc = thearch.anyregalloc(); if(n == N) goto ret; @@ -304,8 +304,8 @@ gen(Node *n) // if there are pending gotos, resolve them all to the current pc. for(p1=lab->gotopc; p1; p1=p2) { - p2 = arch.unpatch(p1); - arch.patch(p1, pc); + p2 = unpatch(p1); + patch(p1, pc); } lab->gotopc = P; if(lab->labelpc == P) @@ -332,9 +332,9 @@ gen(Node *n) // of the label in the OLABEL case above.) lab = newlab(n); if(lab->labelpc != P) - arch.gjmp(lab->labelpc); + gjmp(lab->labelpc); else - lab->gotopc = arch.gjmp(lab->gotopc); + lab->gotopc = gjmp(lab->gotopc); break; case OBREAK: @@ -349,14 +349,14 @@ gen(Node *n) yyerror("invalid break label %S", n->left->sym); break; } - arch.gjmp(lab->breakpc); + gjmp(lab->breakpc); break; } if(breakpc == P) { yyerror("break is not in a loop"); break; } - arch.gjmp(breakpc); + gjmp(breakpc); break; case OCONTINUE: @@ -371,20 +371,20 @@ gen(Node *n) yyerror("invalid continue label %S", n->left->sym); break; } - arch.gjmp(lab->continpc); + gjmp(lab->continpc); break; } if(continpc == P) { yyerror("continue is not in a loop"); break; } - arch.gjmp(continpc); + gjmp(continpc); break; case OFOR: sbreak = breakpc; - p1 = arch.gjmp(P); // goto test - breakpc = arch.gjmp(P); // break: goto done + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done scontin = continpc; continpc = pc; @@ -394,11 +394,11 @@ gen(Node *n) lab->continpc = continpc; } gen(n->nincr); // contin: incr - arch.patch(p1, pc); // test: - arch.bgen(n->ntest, 0, -1, breakpc); // if(!test) goto break + patch(p1, pc); // test: + thearch.bgen(n->ntest, 0, -1, breakpc); // if(!test) goto break genlist(n->nbody); // body - arch.gjmp(continpc); - arch.patch(breakpc, pc); // done: + gjmp(continpc); + patch(breakpc, pc); // done: continpc = scontin; breakpc = sbreak; if(lab) { @@ -408,29 +408,29 @@ gen(Node *n) break; case OIF: - p1 = arch.gjmp(P); // goto test - p2 = arch.gjmp(P); // p2: goto else - arch.patch(p1, pc); // test: - arch.bgen(n->ntest, 0, -n->likely, p2); // if(!test) goto p2 + p1 = gjmp(P); // goto test + p2 = gjmp(P); // p2: goto else + patch(p1, pc); // test: + thearch.bgen(n->ntest, 0, -n->likely, p2); // if(!test) goto p2 genlist(n->nbody); // then - p3 = arch.gjmp(P); // goto done - arch.patch(p2, pc); // else: + p3 = gjmp(P); // goto done + patch(p2, pc); // else: genlist(n->nelse); // else - arch.patch(p3, pc); // done: + patch(p3, pc); // done: break; case OSWITCH: sbreak = breakpc; - p1 = arch.gjmp(P); // goto test - breakpc = arch.gjmp(P); // break: goto done + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done // define break label if((lab = stmtlabel(n)) != L) lab->breakpc = breakpc; - arch.patch(p1, pc); // test: + patch(p1, pc); // test: genlist(n->nbody); // switch(test) body - arch.patch(breakpc, pc); // done: + patch(breakpc, pc); // done: breakpc = sbreak; if(lab != L) lab->breakpc = P; @@ -438,25 +438,21 @@ gen(Node *n) case OSELECT: sbreak = breakpc; - p1 = arch.gjmp(P); // goto test - breakpc = arch.gjmp(P); // break: goto done + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done // define break label if((lab = stmtlabel(n)) != L) lab->breakpc = breakpc; - arch.patch(p1, pc); // test: + patch(p1, pc); // test: genlist(n->nbody); // select() body - arch.patch(breakpc, pc); // done: + patch(breakpc, pc); // done: breakpc = sbreak; if(lab != L) lab->breakpc = P; break; - case OASOP: - arch.cgen_asop(n); - break; - case ODCL: cgen_dcl(n->left); break; @@ -472,11 +468,11 @@ gen(Node *n) break; case OCALLINTER: - arch.cgen_callinter(n, N, 0); + thearch.cgen_callinter(n, N, 0); break; case OCALLFUNC: - arch.cgen_call(n, 0); + thearch.cgen_call(n, 0); break; case OPROC: @@ -489,7 +485,7 @@ gen(Node *n) case ORETURN: case ORETJMP: - arch.cgen_ret(n); + thearch.cgen_ret(n); break; case OCHECKNIL: @@ -502,7 +498,7 @@ gen(Node *n) } ret: - if(arch.anyregalloc() != wasregalloc) { + if(thearch.anyregalloc() != wasregalloc) { dump("node", n); fatal("registers left allocated"); } @@ -536,7 +532,7 @@ cgen_callmeth(Node *n, int proc) if(n2.left->op == ONAME) n2.left->class = PFUNC; - arch.cgen_call(&n2, proc); + thearch.cgen_call(&n2, proc); } /* @@ -554,11 +550,11 @@ cgen_proc(Node *n, int proc) break; case OCALLINTER: - arch.cgen_callinter(n->left, N, proc); + thearch.cgen_callinter(n->left, N, proc); break; case OCALLFUNC: - arch.cgen_call(n->left, proc); + thearch.cgen_call(n->left, proc); break; } @@ -601,7 +597,7 @@ cgen_discard(Node *nr) switch(nr->op) { case ONAME: if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF) - arch.gused(nr); + gused(nr); break; // unary @@ -643,7 +639,7 @@ cgen_discard(Node *nr) default: tempname(&tmp, nr->type); cgen_as(&tmp, nr); - arch.gused(&tmp); + gused(&tmp); } } @@ -705,7 +701,7 @@ clearslim(Node *n) } ullmancalc(&z); - arch.cgen(&z, n); + thearch.cgen(&z, n); } /* @@ -739,10 +735,10 @@ cgen_as(Node *nl, Node *nr) tl = nl->type; if(tl == T) return; - if(arch.isfat(tl)) { + if(isfat(tl)) { if(nl->op == ONAME) gvardef(nl); - arch.clearfat(nl); + thearch.clearfat(nl); return; } clearslim(nl); @@ -753,7 +749,7 @@ cgen_as(Node *nl, Node *nr) if(tl == T) return; - arch.cgen(nr, nl); + thearch.cgen(nr, nl); } /* @@ -773,17 +769,17 @@ cgen_eface(Node *n, Node *res) Node *tmp; tmp = temp(types[tptr]); - arch.cgen(n->right, tmp); + thearch.cgen(n->right, tmp); gvardef(res); dst = *res; dst.type = types[tptr]; dst.xoffset += widthptr; - arch.cgen(tmp, &dst); + thearch.cgen(tmp, &dst); dst.xoffset -= widthptr; - arch.cgen(n->left, &dst); + thearch.cgen(n->left, &dst); } /* @@ -824,7 +820,7 @@ cgen_slice(Node *n, Node *res) if(isnil(n->left)) { tempname(&src, n->left->type); - arch.cgen(n->left, &src); + thearch.cgen(n->left, &src); } else src = *n->left; if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR) @@ -833,11 +829,11 @@ cgen_slice(Node *n, Node *res) if(n->op == OSLICEARR || n->op == OSLICE3ARR) { if(!isptr[n->left->type->etype]) fatal("slicearr is supposed to work on pointer: %+N\n", n); - arch.cgen(&src, base); + thearch.cgen(&src, base); cgen_checknil(base); } else { src.type = types[tptr]; - arch.cgen(&src, base); + thearch.cgen(&src, base); } // committed to the update @@ -846,9 +842,9 @@ cgen_slice(Node *n, Node *res) // compute len and cap. // len = n-i, cap = m-i, and offs = i*width. // computing offs last lets the multiply overwrite i. - arch.cgen(len, tmplen); + thearch.cgen(len, tmplen); if(n->op != OSLICESTR) - arch.cgen(cap, tmpcap); + thearch.cgen(cap, tmpcap); // if new cap != 0 { base += add } // This avoids advancing base past the end of the underlying array/string, @@ -857,40 +853,40 @@ cgen_slice(Node *n, Node *res) // In essence we are replacing x[i:j:k] where i == j == k // or x[i:j] where i == j == cap(x) with x[0:0:0]. if(offs != N) { - p1 = arch.gjmp(P); - p2 = arch.gjmp(P); - arch.patch(p1, pc); + p1 = gjmp(P); + p2 = gjmp(P); + patch(p1, pc); nodconst(&con, tmpcap->type, 0); cmp = nod(OEQ, tmpcap, &con); typecheck(&cmp, Erv); - arch.bgen(cmp, 1, -1, p2); + thearch.bgen(cmp, 1, -1, p2); add = nod(OADD, base, offs); typecheck(&add, Erv); - arch.cgen(add, base); + thearch.cgen(add, base); - arch.patch(p2, pc); + patch(p2, pc); } // dst.array = src.array [ + lo *width ] dst = *res; dst.xoffset += Array_array; dst.type = types[tptr]; - arch.cgen(base, &dst); + thearch.cgen(base, &dst); // dst.len = hi [ - lo ] dst = *res; dst.xoffset += Array_nel; dst.type = types[simtype[TUINT]]; - arch.cgen(tmplen, &dst); + thearch.cgen(tmplen, &dst); if(n->op != OSLICESTR) { // dst.cap = cap [ - lo ] dst = *res; dst.xoffset += Array_cap; dst.type = types[simtype[TUINT]]; - arch.cgen(tmpcap, &dst); + thearch.cgen(tmpcap, &dst); } } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 2aa7838c93..c5ef74586d 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -331,6 +331,7 @@ struct Node // ONAME closure param with PPARAMREF Node* outer; // outer PPARAMREF in nested closure Node* closure; // ONAME/PHEAP <-> ONAME/PPARAMREF + int top; // top context (Ecall, Eproc, etc) // ONAME substitute while inlining Node* inlvar; @@ -598,7 +599,7 @@ enum OCHECKNIL, // emit code to ensure pointer/interface not nil OVARKILL, // variable is dead - // arch-specific registers + // thearch-specific registers OREGISTER, // a register, such as AX. OINDREG, // offset plus indirect of a register, such as 8(SP). @@ -742,6 +743,7 @@ struct Var Node* node; Var* nextinnode; int width; + int id; char name; char etype; char addr; @@ -1075,6 +1077,7 @@ Node* closurebody(NodeList *body); void closurehdr(Node *ntype); void typecheckclosure(Node *func, int top); void capturevars(Node *func); +void transformclosure(Node *func); Node* walkclosure(Node *func, NodeList **init); void typecheckpartialcall(Node*, Node*); Node* walkpartialcall(Node*, NodeList**); @@ -1128,7 +1131,7 @@ void dumpdcl(char *st); Node* embedded(Sym *s, Pkg *pkg); Node* fakethis(void); void funcbody(Node *n); -void funccompile(Node *n, int isclosure); +void funccompile(Node *n); void funchdr(Node *n); Type* functype(Node *this, NodeList *in, NodeList *out); void ifacedcl(Node *n); @@ -1321,7 +1324,9 @@ Sym* typenamesym(Type *t); Sym* tracksym(Type *t); Sym* typesymprefix(char *prefix, Type *t); int haspointers(Type *t); +Type* hmap(Type *t); Type* hiter(Type* t); +Type* mapbucket(Type *t); /* * select.c @@ -1444,6 +1449,7 @@ void warn(char *fmt, ...); void warnl(int line, char *fmt, ...); void yyerror(char *fmt, ...); void yyerrorl(int line, char *fmt, ...); +void adderrorname(Node*); /* * swt.c @@ -1492,7 +1498,7 @@ Node* outervalue(Node*); void usefield(Node*); /* - * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c/plive.c + * thearch-specific ggen.c/gsubr.c/gobj.c/pgen.c/plive.c */ #define P ((Prog*)0) @@ -1514,7 +1520,6 @@ void gvarkill(Node*); void movelarge(NodeList*); void liveness(Node*, Prog*, Sym*, Sym*); void twobitwalktype1(Type*, vlong*, Bvec*); -void nopout(Prog*); #pragma varargck type "B" Mpint* #pragma varargck type "E" int @@ -1558,9 +1563,12 @@ struct Flow { int32 active; // usable by client + int32 id; // sequence number in flow graph int32 rpo; // reverse post ordering uint16 loop; // x5 for every loop uchar refset; // diagnostic generated + + void* data; // for use by client }; struct Graph @@ -1659,61 +1667,83 @@ struct Arch LinkArch *thelinkarch; Typedef *typedefs; + int REGSP; + int REGCTXT; vlong MAXWIDTH; - void (*afunclit)(Addr*, Node*); int (*anyregalloc)(void); void (*betypeinit)(void); void (*bgen)(Node*, int, int, Prog*); void (*cgen)(Node*, Node*); - void (*cgen_asop)(Node*); void (*cgen_call)(Node*, int); void (*cgen_callinter)(Node*, Node*, int); void (*cgen_ret)(Node*); void (*clearfat)(Node*); - void (*clearp)(Prog*); void (*defframe)(Prog*); - int (*dgostringptr)(Sym*, int, char*); - int (*dgostrlitptr)(Sym*, int, Strlit*); - int (*dsname)(Sym*, int, char*, int); - int (*dsymptr)(Sym*, int, Sym*, int); - void (*dumpdata)(void); - void (*dumpit)(char*, Flow*, int); void (*excise)(Flow*); void (*expandchecks)(Prog*); - void (*fixautoused)(Prog*); void (*gclean)(void); - void (*gdata)(Node*, Node*, int); - void (*gdatacomplex)(Node*, Mpcplx*); - void (*gdatastring)(Node*, Strlit*); - void (*ggloblnod)(Node*); - void (*ggloblsym)(Sym*, int32, int8); void (*ginit)(void); Prog* (*gins)(int, Node*, Node*); void (*ginscall)(Node*, int); - Prog* (*gjmp)(Prog*); - void (*gtrack)(Sym*); - void (*gused)(Node*); void (*igen)(Node*, Node*, Node*); - int (*isfat)(Type*); void (*linkarchinit)(void); - void (*markautoused)(Prog*); - void (*naddr)(Node*, Addr*, int); - Plist* (*newplist)(void); - Node* (*nodarg)(Type*, int); - void (*patch)(Prog*, Prog*); + void (*peep)(Prog*); void (*proginfo)(ProgInfo*, Prog*); void (*regalloc)(Node*, Type*, Node*); void (*regfree)(Node*); - void (*regopt)(Prog*); int (*regtyp)(Addr*); int (*sameaddr)(Addr*, Addr*); int (*smallindir)(Addr*, Addr*); int (*stackaddr)(Addr*); - Prog* (*unpatch)(Prog*); + uint64 (*excludedregs)(void); + uint64 (*RtoB)(int); + uint64 (*FtoB)(int); + int (*BtoR)(uint64); + int (*BtoF)(uint64); + int (*optoas)(int, Type*); + uint64 (*doregbits)(int); + char **(*regnames)(int*); }; -EXTERN Arch arch; +void afunclit(Addr*, Node*); +void clearp(Prog*); +int dgostringptr(Sym*, int, char*); +int dgostrlitptr(Sym*, int, Strlit*); +int dsname(Sym*, int, char*, int); +int dsymptr(Sym*, int, Sym*, int); +void dumpdata(void); +void fixautoused(Prog*); +void gdata(Node*, Node*, int); +void gdatacomplex(Node*, Mpcplx*); +void gdatastring(Node*, Strlit*); +void ggloblnod(Node*); +void ggloblsym(Sym*, int32, int8); +Prog* gjmp(Prog*); +void gtrack(Sym*); +void gused(Node*); +int isfat(Type*); +void markautoused(Prog*); +void naddr(Node*, Addr*, int); +Plist* newplist(void); +Node* nodarg(Type*, int); +void patch(Prog*, Prog*); +Prog* unpatch(Prog*); +void datagostring(Strlit *sval, Addr *a); +int ismem(Node*); +int samereg(Node*, Node*); +void regopt(Prog*); +int Tconv(Fmt*); +int Oconv(Fmt*); +Prog* gbranch(int as, Type *t, int likely); +void nodindreg(Node *n, Type *t, int r); +void nodreg(Node *n, Type *t, int r); +Prog* prog(int as); +void datastring(char*, int, Addr*); + +EXTERN int32 pcloc; + +EXTERN Arch thearch; EXTERN Node *newproc; EXTERN Node *deferproc; diff --git a/src/cmd/gc/gsubr.c b/src/cmd/gc/gsubr.c new file mode 100644 index 0000000000..5175ae34f5 --- /dev/null +++ b/src/cmd/gc/gsubr.c @@ -0,0 +1,654 @@ +// Derived from Inferno utils/6c/txt.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/txt.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 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. + +#include <u.h> +#include <libc.h> +#include "go.h" +#include "../../runtime/funcdata.h" +#include "../ld/textflag.h" + +void +ggloblnod(Node *nam) +{ + Prog *p; + + p = thearch.gins(AGLOBL, nam, N); + p->lineno = nam->lineno; + p->from.sym->gotype = linksym(ngotype(nam)); + p->to.sym = nil; + p->to.type = TYPE_CONST; + p->to.offset = nam->type->width; + if(nam->readonly) + p->from3.offset = RODATA; + if(nam->type != T && !haspointers(nam->type)) + p->from3.offset |= NOPTR; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = thearch.gins(AUSEFIELD, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.sym = linksym(s); +} + +void +ggloblsym(Sym *s, int32 width, int8 flags) +{ + Prog *p; + + p = thearch.gins(AGLOBL, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.sym = linksym(s); + p->to.type = TYPE_CONST; + p->to.offset = width; + p->from3.offset = flags; +} + +void +clearp(Prog *p) +{ + nopout(p); + p->as = AEND; + p->pc = pcloc; + pcloc++; +} + +static int ddumped; +static Prog *dfirst; +static Prog *dpc; + +/* + * generate and return proc with p->as = as, + * linked into program. pc is next instruction. + */ +Prog* +prog(int as) +{ + Prog *p; + + if(as == ADATA || as == AGLOBL) { + if(ddumped) + fatal("already dumped data"); + if(dpc == nil) { + dpc = mal(sizeof(*dpc)); + dfirst = dpc; + } + p = dpc; + dpc = mal(sizeof(*dpc)); + p->link = dpc; + } else { + p = pc; + pc = mal(sizeof(*pc)); + clearp(pc); + p->link = pc; + } + + if(lineno == 0) { + if(debug['K']) + warn("prog: line 0"); + } + + p->as = as; + p->lineno = lineno; + return p; +} + +void +dumpdata(void) +{ + ddumped = 1; + if(dfirst == nil) + return; + newplist(); + *pc = *dfirst; + pc = dpc; + clearp(pc); +} + +/* + * 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, int likely) +{ + Prog *p; + + USED(t); + + p = prog(as); + p->to.type = TYPE_BRANCH; + p->to.u.branch = P; + if(as != AJMP && likely != 0 && thearch.thechar != '9') { + p->from.type = TYPE_CONST; + p->from.offset = likely > 0; + } + return p; +} + +/* + * patch previous branch to jump to to. + */ +void +patch(Prog *p, Prog *to) +{ + if(p->to.type != TYPE_BRANCH) + fatal("patch: not a branch"); + p->to.u.branch = to; + p->to.offset = to->pc; +} + +Prog* +unpatch(Prog *p) +{ + Prog *q; + + if(p->to.type != TYPE_BRANCH) + fatal("unpatch: not a branch"); + q = p->to.u.branch; + p->to.u.branch = P; + p->to.offset = 0; + return q; +} + +/* + * start a new Prog list. + */ +Plist* +newplist(void) +{ + Plist *pl; + + pl = linknewplist(ctxt); + + pc = mal(sizeof(*pc)); + clearp(pc); + pl->firstpc = pc; + + return pl; +} + +void +gused(Node *n) +{ + thearch.gins(ANOP, n, N); // used +} + +Prog* +gjmp(Prog *to) +{ + Prog *p; + + p = gbranch(AJMP, T, 0); + if(to != P) + patch(p, to); + return p; +} + +int +isfat(Type *t) +{ + if(t != T) + switch(t->etype) { + case TSTRUCT: + case TARRAY: + case TSTRING: + case TINTER: // maybe remove later + return 1; + } + return 0; +} + +/* + * naddr of func generates code for address of func. + * if using opcode that can take address implicitly, + * call afunclit to fix up the argument. + */ +void +afunclit(Addr *a, Node *n) +{ + if(a->type == TYPE_ADDR && a->name == NAME_EXTERN) { + a->type = TYPE_MEM; + a->sym = linksym(n->sym); + } +} + +/* + * initialize n to be register r of type t. + */ +void +nodreg(Node *n, Type *t, int r) +{ + if(t == T) + fatal("nodreg: t nil"); + + memset(n, 0, sizeof(*n)); + n->op = OREGISTER; + n->addable = 1; + ullmancalc(n); + n->val.u.reg = r; + n->type = t; +} + +/* + * initialize n to be indirect of register r; n is type t. + */ +void +nodindreg(Node *n, Type *t, int r) +{ + nodreg(n, t, r); + n->op = OINDREG; +} + +/* + * Is this node a memory operand? + */ +int +ismem(Node *n) +{ + switch(n->op) { + case OITAB: + case OSPTR: + case OLEN: + case OCAP: + case OINDREG: + case ONAME: + case OPARAM: + case OCLOSUREVAR: + return 1; + case OADDR: + return thearch.thechar == '6' || thearch.thechar == '9'; // because 6g uses PC-relative addressing; TODO(rsc): not sure why 9g too + } + return 0; +} + +// Sweep the prog list to mark any used nodes. +void +markautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL) + continue; + + if (p->from.node) + ((Node*)(p->from.node))->used = 1; + + if (p->to.node) + ((Node*)(p->to.node))->used = 1; + } +} + +// Fixup instructions after allocauto (formerly compactframe) has moved all autos around. +void +fixautoused(Prog *p) +{ + Prog **lp; + + for (lp=&p; (p=*lp) != P; ) { + if (p->as == ATYPE && p->from.node && p->from.name == NAME_AUTO && !((Node*)(p->from.node))->used) { + *lp = p->link; + continue; + } + if ((p->as == AVARDEF || p->as == AVARKILL) && p->to.node && !((Node*)(p->to.node))->used) { + // Cannot remove VARDEF instruction, because - unlike TYPE handled above - + // VARDEFs are interspersed with other code, and a jump might be using the + // VARDEF as a target. Replace with a no-op instead. A later pass will remove + // the no-ops. + nopout(p); + continue; + } + if (p->from.name == NAME_AUTO && p->from.node) + p->from.offset += ((Node*)(p->from.node))->stkdelta; + + if (p->to.name == NAME_AUTO && p->to.node) + p->to.offset += ((Node*)(p->to.node))->stkdelta; + + lp = &p->link; + } +} + +int +samereg(Node *a, Node *b) +{ + if(a == N || b == N) + return 0; + if(a->op != OREGISTER) + return 0; + if(b->op != OREGISTER) + return 0; + if(a->val.u.reg != b->val.u.reg) + return 0; + return 1; +} + +Node* +nodarg(Type *t, int fp) +{ + Node *n; + NodeList *l; + Type *first; + Iter savet; + + // entire argument struct, not just one arg + if(t->etype == TSTRUCT && t->funarg) { + n = nod(ONAME, N, N); + n->sym = lookup(".args"); + n->type = t; + first = structfirst(&savet, &t); + if(first == nil) + fatal("nodarg: bad struct"); + if(first->width == BADWIDTH) + fatal("nodarg: offset not computed for %T", t); + n->xoffset = first->width; + n->addable = 1; + goto fp; + } + + if(t->etype != TFIELD) + fatal("nodarg: not field %T", t); + + if(fp == 1) { + for(l=curfn->dcl; l; l=l->next) { + n = l->n; + if((n->class == PPARAM || n->class == PPARAMOUT) && !isblanksym(t->sym) && n->sym == t->sym) + return n; + } + } + + n = nod(ONAME, N, N); + n->type = t->type; + n->sym = t->sym; + + if(t->width == BADWIDTH) + fatal("nodarg: offset not computed for %T", t); + n->xoffset = t->width; + n->addable = 1; + n->orig = t->nname; + +fp: + // Rewrite argument named _ to __, + // or else the assignment to _ will be + // discarded during code generation. + if(isblank(n)) + n->sym = lookup("__"); + + switch(fp) { + case 0: // output arg + n->op = OINDREG; + n->val.u.reg = thearch.REGSP; + if(thearch.thechar == '5') + n->xoffset += 4; + if(thearch.thechar == '9') + n->xoffset += 8; + break; + + case 1: // input arg + n->class = PPARAM; + break; + + case 2: // offset output arg +fatal("shouldn't be used"); + n->op = OINDREG; + n->val.u.reg = thearch.REGSP; + n->xoffset += types[tptr]->width; + break; + } + n->typecheck = 1; + return n; +} + +/* + * generate code to compute n; + * make a refer to result. + */ +void +naddr(Node *n, Addr *a, int canemitcode) +{ + Sym *s; + + *a = zprog.from; + if(n == N) + return; + + if(n->type != T && n->type->etype != TIDEAL) { + // TODO(rsc): This is undone by the selective clearing of width below, + // to match architectures that were not as aggressive in setting width + // during naddr. Those widths must be cleared to avoid triggering + // failures in gins when it detects real but heretofore latent (and one + // hopes innocuous) type mismatches. + // The type mismatches should be fixed and the clearing below removed. + dowidth(n->type); + a->width = n->type->width; + } + + switch(n->op) { + default: + fatal("naddr: bad %O %D", n->op, a); + break; + + case OREGISTER: + a->type = TYPE_REG; + a->reg = n->val.u.reg; + a->sym = nil; + if(thearch.thechar == '8') // TODO(rsc): Never clear a->width. + a->width = 0; + break; + + case OINDREG: + a->type = TYPE_MEM; + a->reg = n->val.u.reg; + a->sym = linksym(n->sym); + a->offset = n->xoffset; + if(a->offset != (int32)a->offset) + yyerror("offset %lld too large for OINDREG", a->offset); + if(thearch.thechar == '8') // TODO(rsc): Never clear a->width. + a->width = 0; + break; + + case OPARAM: + // n->left is PHEAP ONAME for stack parameter. + // compute address of actual parameter on stack. + a->etype = simtype[n->left->type->etype]; + a->width = n->left->type->width; + a->offset = n->xoffset; + a->sym = linksym(n->left->sym); + a->type = TYPE_MEM; + a->name = NAME_PARAM; + a->node = n->left->orig; + break; + + case OCLOSUREVAR: + if(!curfn->needctxt) + fatal("closurevar without needctxt"); + a->type = TYPE_MEM; + a->reg = thearch.REGCTXT; + a->sym = nil; + a->offset = n->xoffset; + break; + + case OCFUNC: + naddr(n->left, a, canemitcode); + a->sym = linksym(n->left->sym); + break; + + case ONAME: + a->etype = 0; + if(n->type != T) + a->etype = simtype[n->type->etype]; + a->offset = n->xoffset; + s = n->sym; + a->node = n->orig; + //if(a->node >= (Node*)&n) + // fatal("stack node"); + if(s == S) + s = lookup(".noname"); + if(n->method) { + if(n->type != T) + if(n->type->sym != S) + if(n->type->sym->pkg != nil) + s = pkglookup(s->name, n->type->sym->pkg); + } + + a->type = TYPE_MEM; + switch(n->class) { + default: + fatal("naddr: ONAME class %S %d\n", n->sym, n->class); + case PEXTERN: + a->name = NAME_EXTERN; + break; + case PAUTO: + a->name = NAME_AUTO; + break; + case PPARAM: + case PPARAMOUT: + a->name = NAME_PARAM; + break; + case PFUNC: + a->name = NAME_EXTERN; + a->type = TYPE_ADDR; + a->width = widthptr; + s = funcsym(s); + break; + } + a->sym = linksym(s); + break; + + case OLITERAL: + if(thearch.thechar == '8') + a->width = 0; + switch(n->val.ctype) { + default: + fatal("naddr: const %lT", n->type); + break; + case CTFLT: + a->type = TYPE_FCONST; + a->u.dval = mpgetflt(n->val.u.fval); + break; + case CTINT: + case CTRUNE: + a->sym = nil; + a->type = TYPE_CONST; + a->offset = mpgetfix(n->val.u.xval); + break; + case CTSTR: + datagostring(n->val.u.sval, a); + break; + case CTBOOL: + a->sym = nil; + a->type = TYPE_CONST; + a->offset = n->val.u.bval; + break; + case CTNIL: + a->sym = nil; + a->type = TYPE_CONST; + a->offset = 0; + break; + } + break; + + case OADDR: + naddr(n->left, a, canemitcode); + a->etype = tptr; + if(thearch.thechar != '5' && thearch.thechar != '9') // TODO(rsc): Do this even for arm, ppc64. + a->width = widthptr; + if(a->type != TYPE_MEM) + fatal("naddr: OADDR %D (from %O)", a, n->left->op); + a->type = TYPE_ADDR; + break; + + case OITAB: + // itable of interface value + naddr(n->left, a, canemitcode); + if(a->type == TYPE_CONST && a->offset == 0) + break; // itab(nil) + a->etype = tptr; + a->width = widthptr; + break; + + case OSPTR: + // pointer in a string or slice + naddr(n->left, a, canemitcode); + if(a->type == TYPE_CONST && a->offset == 0) + break; // ptr(nil) + a->etype = simtype[tptr]; + a->offset += Array_array; + a->width = widthptr; + break; + + case OLEN: + // len of string or slice + naddr(n->left, a, canemitcode); + if(a->type == TYPE_CONST && a->offset == 0) + break; // len(nil) + a->etype = simtype[TUINT]; + if(thearch.thechar == '9') + a->etype = simtype[TINT]; + a->offset += Array_nel; + if(thearch.thechar != '5') // TODO(rsc): Do this even on arm. + a->width = widthint; + break; + + case OCAP: + // cap of string or slice + naddr(n->left, a, canemitcode); + if(a->type == TYPE_CONST && a->offset == 0) + break; // cap(nil) + a->etype = simtype[TUINT]; + if(thearch.thechar == '9') + a->etype = simtype[TINT]; + a->offset += Array_cap; + if(thearch.thechar != '5') // TODO(rsc): Do this even on arm. + a->width = widthint; + break; + +// case OADD: +// if(n->right->op == OLITERAL) { +// v = n->right->vconst; +// naddr(n->left, a, canemitcode); +// } else +// if(n->left->op == OLITERAL) { +// v = n->left->vconst; +// naddr(n->right, a, canemitcode); +// } else +// goto bad; +// a->offset += v; +// break; + + } +} diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c index 918d37180b..c769ec27f0 100644 --- a/src/cmd/gc/init.c +++ b/src/cmd/gc/init.c @@ -191,5 +191,5 @@ fninit(NodeList *n) typecheck(&fn, Etop); typechecklist(r, Etop); curfn = nil; - funccompile(fn, 0); + funccompile(fn); } diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 042099bd5e..a4b832aa0b 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -95,7 +95,7 @@ enum void usage(void) { - print("usage: %cg [options] file.go...\n", arch.thechar); + print("usage: %cg [options] file.go...\n", thearch.thechar); flagprint(1); exits("usage"); } @@ -140,7 +140,7 @@ doversion(void) sep = ""; if(*p) sep = " "; - print("%cg version %s%s%s\n", arch.thechar, getgoversion(), sep, p); + print("%cg version %s%s%s\n", thearch.thechar, getgoversion(), sep, p); exits(0); } @@ -161,15 +161,15 @@ gcmain(int argc, char *argv[]) // Tell the FPU to handle all exceptions. setfcr(FPPDBL|FPRNR); #endif - // Allow GOARCH=arch.thestring or GOARCH=arch.thestringsuffix, + // Allow GOARCH=thearch.thestring or GOARCH=thearch.thestringsuffix, // but not other values. p = getgoarch(); - if(strncmp(p, arch.thestring, strlen(arch.thestring)) != 0) - sysfatal("cannot use %cg with GOARCH=%s", arch.thechar, p); + if(strncmp(p, thearch.thestring, strlen(thearch.thestring)) != 0) + sysfatal("cannot use %cg with GOARCH=%s", thearch.thechar, p); goarch = p; - arch.linkarchinit(); - ctxt = linknew(arch.thelinkarch); + thearch.linkarchinit(); + ctxt = linknew(thearch.thelinkarch); ctxt->diag = yyerror; ctxt->bso = &bstdout; Binit(&bstdout, 1, OWRITE); @@ -179,6 +179,7 @@ gcmain(int argc, char *argv[]) // pseudo-package, for scoping builtinpkg = mkpkg(newstrlit("go.builtin")); + builtinpkg->prefix = "go.builtin"; // not go%2ebuiltin // pseudo-package, accessed by import "unsafe" unsafepkg = mkpkg(newstrlit("unsafe")); @@ -267,7 +268,7 @@ gcmain(int argc, char *argv[]) flagcount("wb", "enable write barrier", &use_writebarrier); flagcount("x", "debug lexer", &debug['x']); flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']); - if(arch.thechar == '6') + if(thearch.thechar == '6') flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); flagparse(&argc, &argv, usage); @@ -308,7 +309,7 @@ gcmain(int argc, char *argv[]) if(debug['l'] <= 1) debug['l'] = 1 - debug['l']; - if(arch.thechar == '8') { + if(thearch.thechar == '8') { p = getgo386(); if(strcmp(p, "387") == 0) use_sse = 0; @@ -319,7 +320,7 @@ gcmain(int argc, char *argv[]) } fmtinstallgo(); - arch.betypeinit(); + thearch.betypeinit(); if(widthptr == 0) fatal("betypeinit failed"); @@ -404,8 +405,9 @@ gcmain(int argc, char *argv[]) } } - // Phase 4: Decide how to capture variables - // and transform closure bodies accordingly. + // Phase 4: Decide how to capture closed variables. + // This needs to run before escape analysis, + // because variables captured by value do not escape. for(l=xtop; l; l=l->next) { if(l->n->op == ODCLFUNC && l->n->closure) { curfn = l->n; @@ -456,15 +458,26 @@ gcmain(int argc, char *argv[]) // Move large values off stack too. movelarge(xtop); - // Phase 7: Compile top level functions. + // Phase 7: Transform closure bodies to properly reference captured variables. + // This needs to happen before walk, because closures must be transformed + // before walk reaches a call of a closure. + for(l=xtop; l; l=l->next) { + if(l->n->op == ODCLFUNC && l->n->closure) { + curfn = l->n; + transformclosure(l->n); + } + } + curfn = N; + + // Phase 8: Compile top level functions. for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) - funccompile(l->n, 0); + funccompile(l->n); if(nsavederrors+nerrors == 0) fninit(xtop); - // Phase 8: Check external declarations. + // Phase 9: Check external declarations. for(l=externdcl; l; l=l->next) if(l->n->op == ONAME) typecheck(&l->n, Erv); @@ -588,7 +601,7 @@ findpkg(Strlit *name) snprint(namebuf, sizeof(namebuf), "%Z.a", name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%Z.%c", name, arch.thechar); + snprint(namebuf, sizeof(namebuf), "%Z.%c", name, thearch.thechar); if(access(namebuf, 0) >= 0) return 1; return 0; @@ -610,7 +623,7 @@ findpkg(Strlit *name) snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, arch.thechar); + snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, thearch.thechar); if(access(namebuf, 0) >= 0) return 1; } @@ -627,7 +640,7 @@ findpkg(Strlit *name) snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name); if(access(namebuf, 0) >= 0) return 1; - snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, arch.thechar); + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thearch.thechar); if(access(namebuf, 0) >= 0) return 1; } @@ -647,7 +660,7 @@ importfile(Val *f, int line) Biobuf *imp; char *file, *p, *q, *tag; int32 c; - int len; + int n; Strlit *path; char *cleanbuf, *prefix; @@ -745,8 +758,8 @@ importfile(Val *f, int line) } file = strdup(namebuf); - len = strlen(namebuf); - if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') { + n = strlen(namebuf); + if(n > 2 && namebuf[n-2] == '.' && namebuf[n-1] == 'a') { if(!skiptopkgdef(imp)) { yyerror("import %s: not a package file", file); errorexit(); @@ -770,7 +783,7 @@ importfile(Val *f, int line) // assume files move (get installed) // so don't record the full path. - linehist(file + len - path->len - 2, -1, 1); // acts as #pragma lib + linehist(file + n - path->len - 2, -1, 1); // acts as #pragma lib /* * position the input right @@ -974,17 +987,7 @@ l0: rune = c; clen += runetochar(cp+clen, &rune); } - - strlit: - *(int32*)cp = clen-sizeof(int32); // length - do { - cp[clen++] = 0; - } while(clen & MAXALIGN); - yylval.val.u.sval = (Strlit*)cp; - yylval.val.ctype = CTSTR; - DBG("lex: string literal\n"); - strcpy(litbuf, "string literal"); - return LLITERAL; + goto strlit; case '\'': /* '.' */ @@ -1279,7 +1282,7 @@ talph: if(c >= Runeself) { ungetc(c); rune = getr(); - // 0xb7 · is used for internal names + // 0xb7 · is used for internal names if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) yyerror("invalid identifier character U+%04x", rune); cp += runetochar(cp, &rune); @@ -1467,6 +1470,17 @@ caseout: strcpy(litbuf, "literal "); strcat(litbuf, lexbuf); return LLITERAL; + +strlit: + *(int32*)cp = clen-sizeof(int32); // length + do { + cp[clen++] = 0; + } while(clen & MAXALIGN); + yylval.val.u.sval = (Strlit*)cp; + yylval.val.ctype = CTSTR; + DBG("lex: string literal\n"); + strcpy(litbuf, "string literal"); + return LLITERAL; } static void pragcgo(char*); @@ -2270,10 +2284,10 @@ lexfini(void) } // backend-specific builtin types (e.g. int). - for(i=0; arch.typedefs[i].name; i++) { - s = lookup(arch.typedefs[i].name); + for(i=0; thearch.typedefs[i].name; i++) { + s = lookup(thearch.typedefs[i].name); if(s->def == N) { - s->def = typenod(types[arch.typedefs[i].etype]); + s->def = typenod(types[thearch.typedefs[i].etype]); s->origpkg = builtinpkg; } } @@ -2571,6 +2585,6 @@ mkpackage(char* pkgname) p = strrchr(namebuf, '.'); if(p != nil) *p = 0; - outfile = smprint("%s.%c", namebuf, arch.thechar); + outfile = smprint("%s.%c", namebuf, thearch.thechar); } } diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin index 1dab1c9a05..696aa82424 100755 --- a/src/cmd/gc/mkbuiltin +++ b/src/cmd/gc/mkbuiltin @@ -20,6 +20,10 @@ GC=${GOCHAR}g gcc -o mkbuiltin1 mkbuiltin1.c rm -f _builtin.c echo "// AUTO-GENERATED by mkbuiltin; DO NOT EDIT" >>_builtin.c +echo "#include <u.h>" >>_builtin.c +echo "#include <libc.h>" >>_builtin.c +echo '#include "go.h"' >>_builtin.c + for i in runtime unsafe do go tool $GC -A $i.go diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index d33a81e09d..6a0eb2d6d9 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -235,9 +235,9 @@ mppow10flt(Mpflt *a, int p) static void mphextofix(Mpint *a, char *s, int n) { - char *hexdigitp, *end, c; + char c; long d; - int bit; + int bit, hexdigitp, end; while(*s == '0') { s++; @@ -250,9 +250,9 @@ mphextofix(Mpint *a, char *s, int n) return; } - end = s+n-1; - for(hexdigitp=end; hexdigitp>=s; hexdigitp--) { - c = *hexdigitp; + end = n-1; + for(hexdigitp=end; hexdigitp>=0; hexdigitp--) { + c = s[hexdigitp]; if(c >= '0' && c <= '9') d = c-'0'; else if(c >= 'A' && c <= 'F') @@ -334,18 +334,24 @@ mpatoflt(Mpflt *a, char *as) break; } } - if(start == nil) + if(start == nil) { + yyerror("malformed hex constant: %s", as); goto bad; + } mphextofix(&a->val, start, s-start); - if(a->val.ovf) + if(a->val.ovf) { + yyerror("constant too large: %s", as); goto bad; + } a->exp = 0; mpnorm(a); } for(;;) { - switch(c = *s++) { + c = *s++; + switch(c) { default: + yyerror("malformed constant: %s (at %c)", as, c); goto bad; case '-': @@ -357,8 +363,10 @@ mpatoflt(Mpflt *a, char *as) continue; case '.': - if(base == 16) + if(base == 16) { + yyerror("decimal point in hex constant: %s", as); goto bad; + } dp = 1; continue; @@ -414,8 +422,10 @@ mpatoflt(Mpflt *a, char *as) } if(eb) { - if(dp) + if(dp) { + yyerror("decimal point and binary point in constant: %s", as); goto bad; + } mpsetexp(a, a->exp+ex); goto out; } @@ -444,7 +454,6 @@ out: return; bad: - yyerror("constant too large: %s", as); mpmovecflt(a, 0.0); } @@ -483,6 +492,7 @@ mpatofix(Mpint *a, char *as) c = *s++; continue; } + yyerror("malformed decimal constant: %s", as); goto bad; } goto out; @@ -498,6 +508,7 @@ oct: c = *s++; continue; } + yyerror("malformed octal constant: %s", as); goto bad; } goto out; @@ -511,11 +522,14 @@ hex: c = *s; continue; } + yyerror("malformed hex constant: %s", as); goto bad; } mphextofix(a, s0, s-s0); - if(a->ovf) + if(a->ovf) { + yyerror("constant too large: %s", as); goto bad; + } out: if(f) @@ -523,14 +537,14 @@ out: return; bad: - yyerror("constant too large: %s", as); mpmovecfix(a, 0); } int Bconv(Fmt *fp) { - char buf[500], *p; + char buf[500]; + int p; Mpint *xval, q, r, ten, sixteen; int f, digit; @@ -542,8 +556,8 @@ Bconv(Fmt *fp) mpnegfix(&q); } - p = &buf[sizeof(buf)]; - *--p = 0; + p = sizeof(buf); + buf[--p] = 0; if(fp->flags & FmtSharp) { // Hexadecimal mpmovecfix(&sixteen, 16); @@ -551,27 +565,27 @@ Bconv(Fmt *fp) mpdivmodfixfix(&q, &r, &q, &sixteen); digit = mpgetfix(&r); if(digit < 10) - *--p = digit + '0'; + buf[--p] = digit + '0'; else - *--p = digit - 10 + 'A'; + buf[--p] = digit - 10 + 'A'; if(mptestfix(&q) <= 0) break; } - *--p = 'x'; - *--p = '0'; + buf[--p] = 'x'; + buf[--p] = '0'; } else { // Decimal mpmovecfix(&ten, 10); for(;;) { mpdivmodfixfix(&q, &r, &q, &ten); - *--p = mpgetfix(&r) + '0'; + buf[--p] = mpgetfix(&r) + '0'; if(mptestfix(&q) <= 0) break; } } if(f) - *--p = '-'; - return fmtstrcpy(fp, p); + buf[--p] = '-'; + return fmtstrcpy(fp, &buf[p]); } int diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c index fd9f591cea..37aafbb5f5 100644 --- a/src/cmd/gc/mparith2.c +++ b/src/cmd/gc/mparith2.c @@ -14,12 +14,10 @@ static int mplen(Mpint *a) { int i, n; - long *a1; n = -1; - a1 = &a->a[0]; for(i=0; i<Mpprec; i++) { - if(*a1++ != 0) + if(a->a[i] != 0) n = i; } return n+1; @@ -32,19 +30,18 @@ mplen(Mpint *a) static void mplsh(Mpint *a, int quiet) { - long *a1, x; + long x; int i, c; c = 0; - a1 = &a->a[0]; for(i=0; i<Mpprec; i++) { - x = (*a1 << 1) + c; + x = (a->a[i] << 1) + c; c = 0; if(x >= Mpbase) { x -= Mpbase; c = 1; } - *a1++ = x; + a->a[i] = x; } a->ovf = c; if(a->ovf && !quiet) @@ -58,20 +55,17 @@ mplsh(Mpint *a, int quiet) static void mplshw(Mpint *a, int quiet) { - long *a1; int i; - a1 = &a->a[Mpprec-1]; - if(*a1) { + i = Mpprec-1; + if(a->a[i]) { a->ovf = 1; if(!quiet) yyerror("constant shift overflow"); } - for(i=1; i<Mpprec; i++) { - a1[0] = a1[-1]; - a1--; - } - a1[0] = 0; + for(; i > 0; i--) + a->a[i] = a->a[i-1]; + a->a[i] = 0; } // @@ -81,15 +75,14 @@ mplshw(Mpint *a, int quiet) static void mprsh(Mpint *a) { - long *a1, x, lo; + long x, lo; int i, c; c = 0; lo = a->a[0] & 1; - a1 = &a->a[Mpprec]; - for(i=0; i<Mpprec; i++) { - x = *--a1; - *a1 = (x + c) >> 1; + for(i=Mpprec-1; i>=0; i--) { + x = a->a[i]; + a->a[i] = (x + c) >> 1; c = 0; if(x & 1) c = Mpbase; @@ -105,16 +98,14 @@ mprsh(Mpint *a) static void mprshw(Mpint *a) { - long *a1, lo; + long lo; int i; lo = a->a[0]; - a1 = &a->a[0]; - for(i=1; i<Mpprec; i++) { - a1[0] = a1[1]; - a1++; + for(i=0; i<Mpprec-1; i++) { + a->a[i] = a->a[i+1]; } - a1[0] = 0; + a->a[i] = 0; if(a->neg && lo != 0) mpaddcfix(a, -1); } @@ -125,7 +116,7 @@ mprshw(Mpint *a) static int mpcmp(Mpint *a, Mpint *b) { - long x, *a1, *b1; + long x; int i; if(a->ovf || b->ovf) { @@ -134,11 +125,8 @@ mpcmp(Mpint *a, Mpint *b) return 0; } - a1 = &a->a[0] + Mpprec; - b1 = &b->a[0] + Mpprec; - - for(i=0; i<Mpprec; i++) { - x = *--a1 - *--b1; + for(i=Mpprec-1; i>=0; i--) { + x = a->a[i] - b->a[i]; if(x > 0) return +1; if(x < 0) @@ -154,19 +142,18 @@ mpcmp(Mpint *a, Mpint *b) static void mpneg(Mpint *a) { - long x, *a1; + long x; int i, c; - a1 = &a->a[0]; c = 0; for(i=0; i<Mpprec; i++) { - x = -*a1 -c; + x = -a->a[i] -c; c = 0; if(x < 0) { x += Mpbase; c = 1; } - *a1++ = x; + a->a[i] = x; } } @@ -202,7 +189,7 @@ void mpaddfixfix(Mpint *a, Mpint *b, int quiet) { int i, c; - long x, *a1, *b1; + long x; if(a->ovf || b->ovf) { if(nsavederrors+nerrors == 0) @@ -212,20 +199,18 @@ mpaddfixfix(Mpint *a, Mpint *b, int quiet) } c = 0; - a1 = &a->a[0]; - b1 = &b->a[0]; if(a->neg != b->neg) goto sub; // perform a+b for(i=0; i<Mpprec; i++) { - x = *a1 + *b1++ + c; + x = a->a[i] + b->a[i] + c; c = 0; if(x >= Mpbase) { x -= Mpbase; c = 1; } - *a1++ = x; + a->a[i] = x; } a->ovf = c; if(a->ovf && !quiet) @@ -242,26 +227,26 @@ sub: case 1: for(i=0; i<Mpprec; i++) { - x = *a1 - *b1++ - c; + x = a->a[i] - b->a[i] - c; c = 0; if(x < 0) { x += Mpbase; c = 1; } - *a1++ = x; + a->a[i] = x; } break; case -1: a->neg ^= 1; for(i=0; i<Mpprec; i++) { - x = *b1++ - *a1 - c; + x = b->a[i] - a->a[i] - c; c = 0; if(x < 0) { x += Mpbase; c = 1; } - *a1++ = x; + a->a[i] = x; } break; } @@ -272,8 +257,9 @@ mpmulfixfix(Mpint *a, Mpint *b) { int i, j, na, nb; - long *a1, x; + long x; Mpint s, q; + Mpint *c; if(a->ovf || b->ovf) { if(nsavederrors+nerrors == 0) @@ -288,17 +274,17 @@ mpmulfixfix(Mpint *a, Mpint *b) nb = mplen(b); if(na > nb) { mpmovefixfix(&s, a); - a1 = &b->a[0]; + c = b; na = nb; } else { mpmovefixfix(&s, b); - a1 = &a->a[0]; + c = a; } s.neg = 0; mpmovecfix(&q, 0); for(i=0; i<na; i++) { - x = *a1++; + x = c->a[i]; for(j=0; j<Mpscale; j++) { if(x & 1) { if(s.ovf) { @@ -326,7 +312,7 @@ mpmulfract(Mpint *a, Mpint *b) { int i, j; - long *a1, x; + long x; Mpint s, q; if(a->ovf || b->ovf) { @@ -337,16 +323,16 @@ mpmulfract(Mpint *a, Mpint *b) } mpmovefixfix(&s, b); - a1 = &a->a[Mpprec]; s.neg = 0; mpmovecfix(&q, 0); - x = *--a1; + i = Mpprec-1; + x = a->a[i]; if(x != 0) yyerror("mpmulfract not normal"); - for(i=0; i<Mpprec-1; i++) { - x = *--a1; + for(i--; i >= 0; i--) { + x = a->a[i]; if(x == 0) { mprshw(&s); continue; @@ -369,7 +355,7 @@ void mporfixfix(Mpint *a, Mpint *b) { int i; - long x, *a1, *b1; + long x; x = 0; if(a->ovf || b->ovf) { @@ -386,11 +372,9 @@ mporfixfix(Mpint *a, Mpint *b) if(b->neg) mpneg(b); - a1 = &a->a[0]; - b1 = &b->a[0]; for(i=0; i<Mpprec; i++) { - x = *a1 | *b1++; - *a1++ = x; + x = a->a[i] | b->a[i]; + a->a[i] = x; } if(b->neg) @@ -405,7 +389,7 @@ void mpandfixfix(Mpint *a, Mpint *b) { int i; - long x, *a1, *b1; + long x; x = 0; if(a->ovf || b->ovf) { @@ -422,11 +406,9 @@ mpandfixfix(Mpint *a, Mpint *b) if(b->neg) mpneg(b); - a1 = &a->a[0]; - b1 = &b->a[0]; for(i=0; i<Mpprec; i++) { - x = *a1 & *b1++; - *a1++ = x; + x = a->a[i] & b->a[i]; + a->a[i] = x; } if(b->neg) @@ -441,7 +423,7 @@ void mpandnotfixfix(Mpint *a, Mpint *b) { int i; - long x, *a1, *b1; + long x; x = 0; if(a->ovf || b->ovf) { @@ -458,11 +440,9 @@ mpandnotfixfix(Mpint *a, Mpint *b) if(b->neg) mpneg(b); - a1 = &a->a[0]; - b1 = &b->a[0]; for(i=0; i<Mpprec; i++) { - x = *a1 & ~*b1++; - *a1++ = x; + x = a->a[i] & ~b->a[i]; + a->a[i] = x; } if(b->neg) @@ -477,7 +457,7 @@ void mpxorfixfix(Mpint *a, Mpint *b) { int i; - long x, *a1, *b1; + long x; x = 0; if(a->ovf || b->ovf) { @@ -494,11 +474,9 @@ mpxorfixfix(Mpint *a, Mpint *b) if(b->neg) mpneg(b); - a1 = &a->a[0]; - b1 = &b->a[0]; for(i=0; i<Mpprec; i++) { - x = *a1 ^ *b1++; - *a1++ = x; + x = a->a[i] ^ b->a[i]; + a->a[i] = x; } if(b->neg) @@ -585,7 +563,6 @@ void mpmovecfix(Mpint *a, vlong c) { int i; - long *a1; vlong x; a->neg = 0; @@ -597,9 +574,8 @@ mpmovecfix(Mpint *a, vlong c) x = -(uvlong)x; } - a1 = &a->a[0]; for(i=0; i<Mpprec; i++) { - *a1++ = x&Mpmask; + a->a[i] = x&Mpmask; x >>= Mpscale; } } @@ -658,13 +634,11 @@ mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d) static int mpiszero(Mpint *a) { - long *a1; int i; - a1 = &a->a[0] + Mpprec; - for(i=0; i<Mpprec; i++) { - if(*--a1 != 0) + + for(i=Mpprec-1; i>=0; i--) + if(a->a[i] != 0) return 0; - } return 1; } @@ -673,16 +647,15 @@ mpdivfract(Mpint *a, Mpint *b) { Mpint n, d; int i, j, neg; - long *a1, x; + long x; mpmovefixfix(&n, a); // numerator mpmovefixfix(&d, b); // denominator - a1 = &a->a[Mpprec]; // quotient neg = n.neg ^ d.neg; n.neg = 0; d.neg = 0; - for(i=0; i<Mpprec; i++) { + for(i=Mpprec-1; i >= 0; i--) { x = 0; for(j=0; j<Mpscale; j++) { x <<= 1; @@ -693,7 +666,7 @@ mpdivfract(Mpint *a, Mpint *b) } mprsh(&d); } - *--a1 = x; + a->a[i] = x; } a->neg = neg; } diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index a4d470615a..3983f99d6d 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -95,9 +95,9 @@ dumpobj(void) externdcl = tmp; zero = pkglookup("zerovalue", runtimepkg); - arch.ggloblsym(zero, zerosize, DUPOK|RODATA); + ggloblsym(zero, zerosize, DUPOK|RODATA); - arch.dumpdata(); + dumpdata(); writeobj(ctxt, bout); if(writearchive) { @@ -106,7 +106,7 @@ dumpobj(void) if(size&1) Bputc(bout, 0); Bseek(bout, startobj - ArhdrSize, 0); - snprint(namebuf, sizeof namebuf, "_go_.%c", arch.thechar); + snprint(namebuf, sizeof namebuf, "_go_.%c", thearch.thechar); formathdr(arhdr, namebuf, size); Bwrite(bout, arhdr, ArhdrSize); } @@ -133,13 +133,13 @@ dumpglobls(void) continue; dowidth(n->type); - arch.ggloblnod(n); + ggloblnod(n); } for(l=funcsyms; l; l=l->next) { n = l->n; - arch.dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0); - arch.ggloblsym(n->sym, widthptr, DUPOK|RODATA); + dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0); + ggloblsym(n->sym, widthptr, DUPOK|RODATA); } // Do not reprocess funcsyms on next dumpglobls call. @@ -250,7 +250,7 @@ stringsym(char *s, int len) off = 0; // string header - off = arch.dsymptr(sym, off, sym, widthptr+widthint); + off = dsymptr(sym, off, sym, widthptr+widthint); off = duintxx(sym, off, len, widthint); // string data @@ -258,11 +258,11 @@ stringsym(char *s, int len) m = 8; if(m > len-n) m = len-n; - off = arch.dsname(sym, off, s+n, m); + off = dsname(sym, off, s+n, m); } off = duint8(sym, off, 0); // terminating NUL for runtime off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment - arch.ggloblsym(sym, off, DUPOK|RODATA); + ggloblsym(sym, off, DUPOK|RODATA); return sym; } @@ -283,14 +283,216 @@ slicebytes(Node *nam, char *s, int len) m = 8; if(m > len-n) m = len-n; - off = arch.dsname(sym, off, s+n, m); + off = dsname(sym, off, s+n, m); } - arch.ggloblsym(sym, off, NOPTR); + ggloblsym(sym, off, NOPTR); if(nam->op != ONAME) fatal("slicebytes %N", nam); off = nam->xoffset; - off = arch.dsymptr(nam->sym, off, sym, 0); + off = dsymptr(nam->sym, off, sym, 0); off = duintxx(nam->sym, off, len, widthint); duintxx(nam->sym, off, len, widthint); } + +int +dsname(Sym *s, int off, char *t, int n) +{ + Prog *p; + + p = thearch.gins(ADATA, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.offset = off; + p->from.sym = linksym(s); + p->from3.type = TYPE_CONST; + p->from3.offset = n; + + p->to.type = TYPE_SCONST; + memmove(p->to.u.sval, t, n); + return off + n; +} + +/* + * make a refer to the data s, s+len + * emitting DATA if needed. + */ +void +datastring(char *s, int len, Addr *a) +{ + Sym *sym; + + sym = stringsym(s, len); + a->type = TYPE_MEM; + a->name = NAME_EXTERN; + a->sym = linksym(sym); + a->node = sym->def; + a->offset = widthptr+widthint; // skip header + a->etype = simtype[TINT]; +} + +/* + * make a refer to the string sval, + * emitting DATA if needed. + */ +void +datagostring(Strlit *sval, Addr *a) +{ + Sym *sym; + + sym = stringsym(sval->s, sval->len); + a->type = TYPE_MEM; + a->name = NAME_EXTERN; + a->sym = linksym(sym); + a->node = sym->def; + a->offset = 0; // header + a->etype = TSTRING; +} + +void +gdata(Node *nam, Node *nr, int wid) +{ + Prog *p; + + if(nr->op == OLITERAL) { + switch(nr->val.ctype) { + case CTCPLX: + gdatacomplex(nam, nr->val.u.cval); + return; + case CTSTR: + gdatastring(nam, nr->val.u.sval); + return; + } + } + p = thearch.gins(ADATA, nam, nr); + p->from3.type = TYPE_CONST; + p->from3.offset = wid; +} + +void +gdatacomplex(Node *nam, Mpcplx *cval) +{ + Prog *p; + int w; + + w = cplxsubtype(nam->type->etype); + w = types[w]->width; + + p = thearch.gins(ADATA, nam, N); + p->from3.type = TYPE_CONST; + p->from3.offset = w; + p->to.type = TYPE_FCONST; + p->to.u.dval = mpgetflt(&cval->real); + + p = thearch.gins(ADATA, nam, N); + p->from3.type = TYPE_CONST; + p->from3.offset = w; + p->from.offset += w; + p->to.type = TYPE_FCONST; + p->to.u.dval = mpgetflt(&cval->imag); +} + +void +gdatastring(Node *nam, Strlit *sval) +{ + Prog *p; + Node nod1; + + p = thearch.gins(ADATA, nam, N); + datastring(sval->s, sval->len, &p->to); + p->from3.type = TYPE_CONST; + p->from3.offset = types[tptr]->width; + p->to.type = TYPE_ADDR; +//print("%P\n", p); + + nodconst(&nod1, types[TINT], sval->len); + p = thearch.gins(ADATA, nam, &nod1); + p->from3.type = TYPE_CONST; + p->from3.offset = widthint; + p->from.offset += widthptr; +} + +int +dstringptr(Sym *s, int off, char *str) +{ + Prog *p; + + off = rnd(off, widthptr); + p = thearch.gins(ADATA, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.sym = linksym(s); + p->from.offset = off; + p->from3.type = TYPE_CONST; + p->from3.offset = widthptr; + + datastring(str, strlen(str)+1, &p->to); + p->to.type = TYPE_ADDR; + p->to.etype = simtype[TINT]; + off += widthptr; + + return off; +} + +int +dgostrlitptr(Sym *s, int off, Strlit *lit) +{ + Prog *p; + + if(lit == nil) + return duintptr(s, off, 0); + + off = rnd(off, widthptr); + p = thearch.gins(ADATA, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.sym = linksym(s); + p->from.offset = off; + p->from3.type = TYPE_CONST; + p->from3.offset = widthptr; + datagostring(lit, &p->to); + p->to.type = TYPE_ADDR; + p->to.etype = simtype[TINT]; + off += widthptr; + + return off; +} + +int +dgostringptr(Sym *s, int off, char *str) +{ + int n; + Strlit *lit; + + if(str == nil) + return duintptr(s, off, 0); + + n = strlen(str); + lit = mal(sizeof *lit + n); + strcpy(lit->s, str); + lit->len = n; + return dgostrlitptr(s, off, lit); +} + +int +dsymptr(Sym *s, int off, Sym *x, int xoff) +{ + Prog *p; + + off = rnd(off, widthptr); + + p = thearch.gins(ADATA, N, N); + p->from.type = TYPE_MEM; + p->from.name = NAME_EXTERN; + p->from.sym = linksym(s); + p->from.offset = off; + p->from3.type = TYPE_CONST; + p->from3.offset = widthptr; + p->to.type = TYPE_ADDR; + p->to.name = NAME_EXTERN; + p->to.sym = linksym(x); + p->to.offset = xoff; + off += widthptr; + + return off; +} diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index ec1628651e..3df78e7f9a 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -30,7 +30,7 @@ makefuncdatasym(char *namefmt, int64 funcdatakind) pnod = newname(sym); pnod->class = PEXTERN; nodconst(&nod, types[TINT32], funcdatakind); - arch.gins(AFUNCDATA, &nod, pnod); + thearch.gins(AFUNCDATA, &nod, pnod); return sym; } @@ -39,7 +39,7 @@ makefuncdatasym(char *namefmt, int64 funcdatakind) // where a complete initialization (definition) of a variable begins. // Since the liveness analysis can see initialization of single-word // variables quite easy, gvardef is usually only called for multi-word -// or 'fat' variables, those satisfying arch.isfat(n->type). +// or 'fat' variables, those satisfying isfat(n->type). // However, gvardef is also called when a non-fat variable is initialized // via a block move; the only time this happens is when you have // return f() @@ -103,7 +103,7 @@ gvardefx(Node *n, int as) case PAUTO: case PPARAM: case PPARAMOUT: - arch.gins(as, N, n); + thearch.gins(as, N, n); } } @@ -161,6 +161,7 @@ compile(Node *fn) Iter save; vlong oldstksize; NodeList *l; + Node *nam; Sym *gcargs; Sym *gclocals; @@ -223,13 +224,16 @@ compile(Node *fn) continpc = P; breakpc = P; - pl = arch.newplist(); + pl = newplist(); pl->name = linksym(curfn->nname->sym); setlineno(curfn); nodconst(&nod1, types[TINT32], 0); - ptxt = arch.gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); + nam = curfn->nname; + if(isblank(nam)) + nam = N; + ptxt = thearch.gins(ATEXT, nam, &nod1); if(fn->dupok) ptxt->from3.offset |= DUPOK; if(fn->wrapper) @@ -247,15 +251,15 @@ compile(Node *fn) ptxt->from3.offset |= WRAPPER; } - arch.afunclit(&ptxt->from, curfn->nname); + afunclit(&ptxt->from, curfn->nname); - arch.ginit(); + thearch.ginit(); gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps); for(t=curfn->paramfld; t; t=t->down) - arch.gtrack(tracksym(t->type)); + gtrack(tracksym(t->type)); for(l=fn->dcl; l; l=l->next) { n = l->n; @@ -266,7 +270,7 @@ compile(Node *fn) case PPARAM: case PPARAMOUT: nodconst(&nod1, types[TUINTPTR], l->n->type->width); - p = arch.gins(ATYPE, l->n, &nod1); + p = thearch.gins(ATYPE, l->n, &nod1); p->from.gotype = linksym(ngotype(l->n)); break; } @@ -274,7 +278,7 @@ compile(Node *fn) genlist(curfn->enter); genlist(curfn->nbody); - arch.gclean(); + thearch.gclean(); checklabels(); if(nerrors != 0) goto ret; @@ -282,18 +286,18 @@ compile(Node *fn) lineno = curfn->endlineno; if(curfn->type->outtuple != 0) - arch.ginscall(throwreturn, 0); + thearch.ginscall(throwreturn, 0); - arch.ginit(); + thearch.ginit(); // TODO: Determine when the final cgen_ret can be omitted. Perhaps always? - arch.cgen_ret(nil); + thearch.cgen_ret(nil); if(hasdefer) { // deferreturn pretends to have one uintptr argument. // Reserve space for it so stack scanner is happy. if(maxarg < widthptr) maxarg = widthptr; } - arch.gclean(); + thearch.gclean(); if(nerrors != 0) goto ret; @@ -302,10 +306,10 @@ compile(Node *fn) fixjmp(ptxt); if(!debug['N'] || debug['R'] || debug['P']) { - arch.regopt(ptxt); + regopt(ptxt); nilopt(ptxt); } - arch.expandchecks(ptxt); + thearch.expandchecks(ptxt); oldstksize = stksize; allocauto(ptxt); @@ -325,9 +329,9 @@ compile(Node *fn) gcsymdup(gcargs); gcsymdup(gclocals); - arch.defframe(ptxt); + thearch.defframe(ptxt); - if(0) + if(debug['f']) frame(0); // Remove leftover instrumentation from the instruction stream. @@ -369,7 +373,7 @@ emitptrargsmap(void) for(j = 0; j < bv->n; j += 32) off = duint32(sym, off, bv->b[j/32]); } - arch.ggloblsym(sym, off, RODATA); + ggloblsym(sym, off, RODATA); free(bv); } @@ -438,7 +442,7 @@ allocauto(Prog* ptxt) if (ll->n->class == PAUTO) ll->n->used = 0; - arch.markautoused(ptxt); + markautoused(ptxt); listsort(&curfn->dcl, cmpstackvar); @@ -448,7 +452,7 @@ allocauto(Prog* ptxt) if (n->class == PAUTO && n->op == ONAME && !n->used) { // No locals used at all curfn->dcl = nil; - arch.fixautoused(ptxt); + fixautoused(ptxt); return; } @@ -469,13 +473,13 @@ allocauto(Prog* ptxt) dowidth(n->type); w = n->type->width; - if(w >= arch.MAXWIDTH || w < 0) + if(w >= thearch.MAXWIDTH || w < 0) fatal("bad width"); stksize += w; stksize = rnd(stksize, n->type->align); if(haspointers(n->type)) stkptrsize = stksize; - if(arch.thechar == '5' || arch.thechar == '9') + if(thearch.thechar == '5' || thearch.thechar == '9') stksize = rnd(stksize, widthptr); if(stksize >= (1ULL<<31)) { setlineno(curfn); @@ -486,7 +490,7 @@ allocauto(Prog* ptxt) stksize = rnd(stksize, widthreg); stkptrsize = rnd(stkptrsize, widthreg); - arch.fixautoused(ptxt); + fixautoused(ptxt); // The debug information needs accurate offsets on the symbols. for(ll = curfn->dcl; ll != nil; ll=ll->next) { @@ -532,12 +536,12 @@ cgen_checknil(Node *n) dump("checknil", n); fatal("bad checknil"); } - if(((arch.thechar == '5' || arch.thechar == '9') && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) { - arch.regalloc(®, types[tptr], n); - arch.cgen(n, ®); - arch.gins(ACHECKNIL, ®, N); - arch.regfree(®); + if(((thearch.thechar == '5' || thearch.thechar == '9') && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) { + thearch.regalloc(®, types[tptr], n); + thearch.cgen(n, ®); + thearch.gins(ACHECKNIL, ®, N); + thearch.regfree(®); return; } - arch.gins(ACHECKNIL, n, N); + thearch.gins(ACHECKNIL, n, N); } diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c index 0461085b5b..c0d1e57932 100644 --- a/src/cmd/gc/plive.c +++ b/src/cmd/gc/plive.c @@ -677,7 +677,7 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit) bvresetall(varkill); bvresetall(avarinit); - arch.proginfo(&info, prog); + thearch.proginfo(&info, prog); if(prog->as == ARET) { // Return instructions implicitly read all the arguments. For // the sake of correctness, out arguments must be read. For the @@ -701,7 +701,7 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit) // If we added it to uevar too, we'd not see any kill // and decide that the varible was live entry, which it is not. // So only use uevar in the non-addrtaken case. - // The p->to.type == arch.D_NONE limits the bvset to + // The p->to.type == thearch.D_NONE limits the bvset to // non-tail-call return instructions; see note above // the for loop for details. if(!node->addrtaken && prog->to.type == TYPE_NONE) @@ -744,7 +744,7 @@ progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit) if(info.flags & (LeftRead | LeftAddr)) bvset(uevar, pos); if(info.flags & LeftWrite) - if(from->node != nil && !arch.isfat(((Node*)(from->node))->type)) + if(from->node != nil && !isfat(((Node*)(from->node))->type)) bvset(varkill, pos); } } @@ -780,7 +780,7 @@ Next: if((info.flags & RightRead) || (info.flags & (RightAddr|RightWrite)) == RightAddr) bvset(uevar, pos); if(info.flags & RightWrite) - if(to->node != nil && (!arch.isfat(((Node*)(to->node))->type) || prog->as == AVARDEF)) + if(to->node != nil && (!isfat(((Node*)(to->node))->type) || prog->as == AVARDEF)) bvset(varkill, pos); } } @@ -1218,7 +1218,7 @@ unlinkedprog(int as) Prog *p; p = mal(sizeof(*p)); - arch.clearp(p); + clearp(p); p->as = as; return p; } @@ -1235,8 +1235,8 @@ newpcdataprog(Prog *prog, int32 index) nodconst(&to, types[TINT32], index); pcdata = unlinkedprog(APCDATA); pcdata->lineno = prog->lineno; - arch.naddr(&from, &pcdata->from, 0); - arch.naddr(&to, &pcdata->to, 0); + naddr(&from, &pcdata->from, 0); + naddr(&to, &pcdata->to, 0); return pcdata; } @@ -1909,15 +1909,15 @@ static void twobitwritesymbol(Array *arr, Sym *sym) { Bvec *bv; - int off, i, j, len; + int off, i, j, n; uint32 word; - len = arraylength(arr); + n = arraylength(arr); off = 0; off += 4; // number of bitmaps, to fill in later bv = *(Bvec**)arrayget(arr, 0); off = duint32(sym, off, bv->n); // number of bits in each bitmap - for(i = 0; i < len; i++) { + for(i = 0; i < n; i++) { // bitmap words bv = *(Bvec**)arrayget(arr, i); if(bv == nil) @@ -1932,7 +1932,7 @@ twobitwritesymbol(Array *arr, Sym *sym) } } duint32(sym, 0, i); // number of bitmaps - arch.ggloblsym(sym, off, RODATA); + ggloblsym(sym, off, RODATA); } static void diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c index f71702431a..b02d58e663 100644 --- a/src/cmd/gc/popt.c +++ b/src/cmd/gc/popt.c @@ -35,6 +35,7 @@ #include <u.h> #include <libc.h> #include "go.h" +#include "popt.h" // p is a call instruction. Does the call fail to return? int @@ -202,7 +203,7 @@ fixjmp(Prog *firstp) // Control flow analysis. The Flow structures hold predecessor and successor // information as well as basic loop analysis. // -// graph = flowstart(firstp, sizeof(Flow)); +// graph = flowstart(firstp, 0); // ... use flow graph ... // flowend(graph); // free graph // @@ -214,25 +215,26 @@ fixjmp(Prog *firstp) // f->p1 and this list: // // for(f2 = f->p2; f2 != nil; f2 = f2->p2link) -// -// Often the Flow struct is embedded as the first field inside a larger struct S. -// In that case casts are needed to convert Flow* to S* in many places but the -// idea is the same. Pass sizeof(S) instead of sizeof(Flow) to flowstart. +// +// The size argument to flowstart specifies an amount of zeroed memory +// to allocate in every f->data field, for use by the client. +// If size == 0, f->data will be nil. Graph* flowstart(Prog *firstp, int size) { - int nf; + int id, nf; Flow *f, *f1, *start, *last; Graph *graph; Prog *p; ProgInfo info; + char *data; // Count and mark instructions to annotate. nf = 0; for(p = firstp; p != P; p = p->link) { p->opt = nil; // should be already, but just in case - arch.proginfo(&info, p); + thearch.proginfo(&info, p); if(info.flags & Skip) continue; p->opt = (void*)1; @@ -248,12 +250,16 @@ flowstart(Prog *firstp, int size) } // Allocate annotations and assign to instructions. - graph = calloc(sizeof *graph + size*nf, 1); + graph = calloc(sizeof *graph + sizeof(Flow)*nf + size*nf, 1); if(graph == nil) fatal("out of memory"); start = (Flow*)(graph+1); last = nil; f = start; + data = (char*)(f+nf); + if(size == 0) + data = nil; + id = 0; for(p = firstp; p != P; p = p->link) { if(p->opt == nil) continue; @@ -262,14 +268,17 @@ flowstart(Prog *firstp, int size) if(last) last->link = f; last = f; - - f = (Flow*)((uchar*)f + size); + f->data = data; + f->id = id; + f++; + id++; + data += size; } // Fill in pred/succ information. for(f = start; f != nil; f = f->link) { p = f->prog; - arch.proginfo(&info, p); + thearch.proginfo(&info, p); if(!(info.flags & Break)) { f1 = f->link; f->s1 = f1; @@ -383,10 +392,6 @@ loophead(int32 *idom, Flow *r) return 0; } -enum { - LOOP = 3, -}; - static void loopmark(Flow **rpo2r, int32 head, Flow *r) { @@ -498,13 +503,12 @@ uniqs(Flow *r) // ACM TOPLAS 1999. typedef struct TempVar TempVar; -typedef struct TempFlow TempFlow; struct TempVar { Node *node; - TempFlow *def; // definition of temp var - TempFlow *use; // use list, chained through TempFlow.uselink + Flow *def; // definition of temp var + Flow *use; // use list, chained through Flow.data TempVar *freelink; // next free temp in Type.opt list TempVar *merge; // merge var with this one vlong start; // smallest Prog.pc in live range @@ -513,12 +517,6 @@ struct TempVar uchar removed; // removed from program }; -struct TempFlow -{ - Flow f; - TempFlow *uselink; -}; - static int startcmp(const void *va, const void *vb) { @@ -531,6 +529,14 @@ startcmp(const void *va, const void *vb) return -1; if(a->start > b->start) return +1; + // Order what's left by id or symbol name, + // just so that sort is forced into a specific ordering, + // so that the result of the sort does not depend on + // the sort implementation. + if(a->def != b->def) + return a->def->id - b->def->id; + if(a->node != b->node) + return strcmp(a->node->sym->name, b->node->sym->name); return 0; } @@ -541,15 +547,15 @@ canmerge(Node *n) return n->class == PAUTO && strncmp(n->sym->name, "autotmp", 7) == 0; } -static void mergewalk(TempVar*, TempFlow*, uint32); -static void varkillwalk(TempVar*, TempFlow*, uint32); +static void mergewalk(TempVar*, Flow*, uint32); +static void varkillwalk(TempVar*, Flow*, uint32); void mergetemp(Prog *firstp) { int i, j, nvar, ninuse, nfree, nkill; TempVar *var, *v, *v1, **bystart, **inuse; - TempFlow *r; + Flow *f; NodeList *l, **lp; Node *n; Prog *p, *p1; @@ -558,9 +564,9 @@ mergetemp(Prog *firstp) int32 gen; Graph *g; - enum { Debug = 0 }; + enum { debugmerge = 1 }; - g = flowstart(firstp, sizeof(TempFlow)); + g = flowstart(firstp, 0); if(g == nil) return; @@ -585,9 +591,9 @@ mergetemp(Prog *firstp) // We assume that the earliest reference to a temporary is its definition. // This is not true of variables in general but our temporaries are all // single-use (that's why we have so many!). - for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { - p = r->f.prog; - arch.proginfo(&info, p); + for(f = g->start; f != nil; f = f->link) { + p = f->prog; + thearch.proginfo(&info, p); if(p->from.node != N && ((Node*)(p->from.node))->opt && p->to.node != N && ((Node*)(p->to.node))->opt) fatal("double node %P", p); @@ -598,32 +604,33 @@ mergetemp(Prog *firstp) v = n->opt; if(v != nil) { if(v->def == nil) - v->def = r; - r->uselink = v->use; - v->use = r; + v->def = f; + f->data = v->use; + v->use = f; if(n == p->from.node && (info.flags & LeftAddr)) v->addr = 1; } } - if(Debug > 1) - arch.dumpit("before", g->start, 0); + if(debugmerge > 1 && debug['v']) + dumpit("before", g->start, 0); nkill = 0; // Special case. - for(v = var; v < var+nvar; v++) { + for(i = 0; i < nvar; i++) { + v = &var[i]; if(v->addr) continue; // Used in only one instruction, which had better be a write. - if((r = v->use) != nil && r->uselink == nil) { - p = r->f.prog; - arch.proginfo(&info, p); + if((f = v->use) != nil && (Flow*)f->data == nil) { + p = f->prog; + thearch.proginfo(&info, p); if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) { p->as = ANOP; p->to = zprog.to; v->removed = 1; - if(Debug) + if(debugmerge > 0 && debug['v']) print("drop write-only %S\n", v->node->sym); } else fatal("temp used and not set: %P", p); @@ -633,11 +640,11 @@ mergetemp(Prog *firstp) // Written in one instruction, read in the next, otherwise unused, // no jumps to the next instruction. Happens mainly in 386 compiler. - if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) { - p = r->f.prog; - arch.proginfo(&info, p); - p1 = r->f.link->prog; - arch.proginfo(&info1, p1); + if((f = v->use) != nil && f->link == (Flow*)f->data && (Flow*)((Flow*)f->data)->data == nil && uniqp(f->link) == f) { + p = f->prog; + thearch.proginfo(&info, p); + p1 = f->link->prog; + thearch.proginfo(&info1, p1); enum { SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD, }; @@ -645,9 +652,9 @@ mergetemp(Prog *firstp) !((info.flags|info1.flags) & (LeftAddr|RightAddr)) && (info.flags & SizeAny) == (info1.flags & SizeAny)) { p1->from = p->from; - arch.excise(&r->f); + thearch.excise(f); v->removed = 1; - if(Debug) + if(debugmerge > 0 && debug['v']) print("drop immediate-use %S\n", v->node->sym); } nkill++; @@ -657,16 +664,17 @@ mergetemp(Prog *firstp) // Traverse live range of each variable to set start, end. // Each flood uses a new value of gen so that we don't have - // to clear all the r->f.active words after each variable. + // to clear all the r->active words after each variable. gen = 0; - for(v = var; v < var+nvar; v++) { + for(i = 0; i < nvar; i++) { + v = &var[i]; gen++; - for(r = v->use; r != nil; r = r->uselink) - mergewalk(v, r, gen); + for(f = v->use; f != nil; f = (Flow*)f->data) + mergewalk(v, f, gen); if(v->addr) { gen++; - for(r = v->use; r != nil; r = r->uselink) - varkillwalk(v, r, gen); + for(f = v->use; f != nil; f = (Flow*)f->data) + varkillwalk(v, f, gen); } } @@ -687,6 +695,9 @@ mergetemp(Prog *firstp) nfree = nvar; for(i=0; i<nvar; i++) { v = bystart[i]; + if(debugmerge > 0 && debug['v']) + print("consider %#N: removed=%d\n", v->node, v->removed); + if(v->removed) continue; @@ -696,10 +707,14 @@ mergetemp(Prog *firstp) inuse[--nfree] = v1; } + if(debugmerge > 0 && debug['v']) + print("consider %#N: removed=%d nfree=%d nvar=%d\n", v->node, v->removed, nfree, nvar); // Find old temp to reuse if possible. t = v->node->type; for(j=nfree; j<nvar; j++) { v1 = inuse[j]; + if(debugmerge > 0 && debug['v']) + print("consider %#N: maybe %#N: type=%T,%T addrtaken=%d,%d\n", v->node, v1->node, t, v1->node->type, v->node->addrtaken, v1->node->addrtaken); // Require the types to match but also require the addrtaken bits to match. // If a variable's address is taken, that disables registerization for the individual // words of the variable (for example, the base,len,cap of a slice). @@ -725,9 +740,10 @@ mergetemp(Prog *firstp) inuse[j] = v; } - if(Debug) { + if(debugmerge > 0 && debug['v']) { print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill); - for(v=var; v<var+nvar; v++) { + for(i = 0; i < nvar; i++) { + v = &var[i]; print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end); if(v->addr) print(" addr=1"); @@ -735,18 +751,18 @@ mergetemp(Prog *firstp) print(" dead=1"); if(v->merge) print(" merge %#N", v->merge->node); - if(v->start == v->end) - print(" %P", v->def->f.prog); + if(v->start == v->end && v->def != nil) + print(" %P", v->def->prog); print("\n"); } - if(Debug > 1) - arch.dumpit("after", g->start, 0); + if(debugmerge > 1 && debug['v']) + dumpit("after", g->start, 0); } // Update node references to use merged temporaries. - for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { - p = r->f.prog; + for(f = g->start; f != nil; f = f->link) { + p = f->prog; if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil) p->from.node = v->merge->node; if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil) @@ -766,8 +782,9 @@ mergetemp(Prog *firstp) } // Clear aux structures. - for(v=var; v<var+nvar; v++) - v->node->opt = nil; + for(i = 0; i < nvar; i++) + var[i].node->opt = nil; + free(var); free(bystart); free(inuse); @@ -775,40 +792,40 @@ mergetemp(Prog *firstp) } static void -mergewalk(TempVar *v, TempFlow *r0, uint32 gen) +mergewalk(TempVar *v, Flow *f0, uint32 gen) { Prog *p; - TempFlow *r1, *r, *r2; + Flow *f1, *f, *f2; - for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) { - if(r1->f.active == gen) + for(f1 = f0; f1 != nil; f1 = f1->p1) { + if(f1->active == gen) break; - r1->f.active = gen; - p = r1->f.prog; + f1->active = gen; + p = f1->prog; if(v->end < p->pc) v->end = p->pc; - if(r1 == v->def) { + if(f1 == v->def) { v->start = p->pc; break; } } - for(r = r0; r != r1; r = (TempFlow*)r->f.p1) - for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link) - mergewalk(v, r2, gen); + for(f = f0; f != f1; f = f->p1) + for(f2 = f->p2; f2 != nil; f2 = f2->p2link) + mergewalk(v, f2, gen); } static void -varkillwalk(TempVar *v, TempFlow *r0, uint32 gen) +varkillwalk(TempVar *v, Flow *f0, uint32 gen) { Prog *p; - TempFlow *r1, *r; + Flow *f1, *f; - for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.s1) { - if(r1->f.active == gen) + for(f1 = f0; f1 != nil; f1 = f1->s1) { + if(f1->active == gen) break; - r1->f.active = gen; - p = r1->f.prog; + f1->active = gen; + p = f1->prog; if(v->end < p->pc) v->end = p->pc; if(v->start > p->pc) @@ -817,8 +834,8 @@ varkillwalk(TempVar *v, TempFlow *r0, uint32 gen) break; } - for(r = r0; r != r1; r = (TempFlow*)r->f.s1) - varkillwalk(v, (TempFlow*)r->f.s2, gen); + for(f = f0; f != f1; f = f->s1) + varkillwalk(v, f->s2, gen); } // Eliminate redundant nil pointer checks. @@ -836,62 +853,58 @@ varkillwalk(TempVar *v, TempFlow *r0, uint32 gen) // each load. typedef struct NilVar NilVar; -typedef struct NilFlow NilFlow; -struct NilFlow { - Flow f; - int kill; -}; +static void nilwalkback(Flow *rcheck); +static void nilwalkfwd(Flow *rcheck); -static void nilwalkback(NilFlow *rcheck); -static void nilwalkfwd(NilFlow *rcheck); +static int killed; // f->data is either nil or &killed void nilopt(Prog *firstp) { - NilFlow *r; + Flow *f; Prog *p; Graph *g; int ncheck, nkill; - g = flowstart(firstp, sizeof(NilFlow)); + g = flowstart(firstp, 0); if(g == nil) return; if(debug_checknil > 1 /* || strcmp(curfn->nname->sym->name, "f1") == 0 */) - arch.dumpit("nilopt", g->start, 0); + dumpit("nilopt", g->start, 0); ncheck = 0; nkill = 0; - for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) { - p = r->f.prog; - if(p->as != ACHECKNIL || !arch.regtyp(&p->from)) + for(f = g->start; f != nil; f = f->link) { + p = f->prog; + if(p->as != ACHECKNIL || !thearch.regtyp(&p->from)) continue; ncheck++; - if(arch.stackaddr(&p->from)) { + if(thearch.stackaddr(&p->from)) { if(debug_checknil && p->lineno > 1) warnl(p->lineno, "removed nil check of SP address"); - r->kill = 1; + f->data = &killed; continue; } - nilwalkfwd(r); - if(r->kill) { + nilwalkfwd(f); + if(f->data != nil) { if(debug_checknil && p->lineno > 1) warnl(p->lineno, "removed nil check before indirect"); continue; } - nilwalkback(r); - if(r->kill) { + nilwalkback(f); + if(f->data != nil) { if(debug_checknil && p->lineno > 1) warnl(p->lineno, "removed repeated nil check"); continue; } } - for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) { - if(r->kill) { + for(f = g->start; f != nil; f = f->link) { + if(f->data != nil) { nkill++; - arch.excise(&r->f); + thearch.excise(f); } } @@ -902,72 +915,72 @@ nilopt(Prog *firstp) } static void -nilwalkback(NilFlow *rcheck) +nilwalkback(Flow *fcheck) { Prog *p; ProgInfo info; - NilFlow *r; + Flow *f; - for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) { - p = r->f.prog; - arch.proginfo(&info, p); - if((info.flags & RightWrite) && arch.sameaddr(&p->to, &rcheck->f.prog->from)) { + for(f = fcheck; f != nil; f = uniqp(f)) { + p = f->prog; + thearch.proginfo(&info, p); + if((info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { // Found initialization of value we're checking for nil. // without first finding the check, so this one is unchecked. return; } - if(r != rcheck && p->as == ACHECKNIL && arch.sameaddr(&p->from, &rcheck->f.prog->from)) { - rcheck->kill = 1; + if(f != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) { + fcheck->data = &killed; return; } } // Here is a more complex version that scans backward across branches. - // It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason - // to keep the check (setting rcheck->kill = 0). + // It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason + // to keep the check (setting fcheck->kill = 0). // It doesn't handle copying of aggregates as well as I would like, // nor variables with their address taken, // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. /* - for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) { - if(r1->f.active == gen) + for(f1 = f0; f1 != nil; f1 = f1->p1) { + if(f1->active == gen) break; - r1->f.active = gen; - p = r1->f.prog; + f1->active = gen; + p = f1->prog; // If same check, stop this loop but still check // alternate predecessors up to this point. - if(r1 != rcheck && p->as == ACHECKNIL && arch.sameaddr(&p->from, &rcheck->f.prog->from)) + if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) break; - arch.proginfo(&info, p); - if((info.flags & RightWrite) && arch.sameaddr(&p->to, &rcheck->f.prog->from)) { + thearch.proginfo(&info, p); + if((info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { // Found initialization of value we're checking for nil. // without first finding the check, so this one is unchecked. - rcheck->kill = 0; + fcheck->kill = 0; return; } - if(r1->f.p1 == nil && r1->f.p2 == nil) { - print("lost pred for %P\n", rcheck->f.prog); - for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) { - arch.proginfo(&info, r1->f.prog); - print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, arch.sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from); + if(f1->p1 == nil && f1->p2 == nil) { + print("lost pred for %P\n", fcheck->prog); + for(f1=f0; f1!=nil; f1=f1->p1) { + thearch.proginfo(&info, f1->prog); + print("\t%P %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from); } fatal("lost pred trail"); } } - for(r = r0; r != r1; r = (NilFlow*)r->f.p1) - for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link) - nilwalkback(rcheck, r2, gen); + for(f = f0; f != f1; f = f->p1) + for(f2 = f->p2; f2 != nil; f2 = f2->p2link) + nilwalkback(fcheck, f2, gen); */ } static void -nilwalkfwd(NilFlow *rcheck) +nilwalkfwd(Flow *fcheck) { - NilFlow *r, *last; + Flow *f, *last; Prog *p; ProgInfo info; @@ -979,16 +992,16 @@ nilwalkfwd(NilFlow *rcheck) // _ = *x // should panic // for {} // no writes but infinite loop may be considered visible last = nil; - for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) { - p = r->f.prog; - arch.proginfo(&info, p); + for(f = uniqs(fcheck); f != nil; f = uniqs(f)) { + p = f->prog; + thearch.proginfo(&info, p); - if((info.flags & LeftRead) && arch.smallindir(&p->from, &rcheck->f.prog->from)) { - rcheck->kill = 1; + if((info.flags & LeftRead) && thearch.smallindir(&p->from, &fcheck->prog->from)) { + fcheck->data = &killed; return; } - if((info.flags & (RightRead|RightWrite)) && arch.smallindir(&p->to, &rcheck->f.prog->from)) { - rcheck->kill = 1; + if((info.flags & (RightRead|RightWrite)) && thearch.smallindir(&p->to, &fcheck->prog->from)) { + fcheck->data = &killed; return; } @@ -996,17 +1009,14 @@ nilwalkfwd(NilFlow *rcheck) if(p->as == ACHECKNIL) return; // Stop if value is lost. - if((info.flags & RightWrite) && arch.sameaddr(&p->to, &rcheck->f.prog->from)) + if((info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) return; // Stop if memory write. - if((info.flags & RightWrite) && !arch.regtyp(&p->to)) + if((info.flags & RightWrite) && !thearch.regtyp(&p->to)) return; // Stop if we jump backward. - // This test is valid because all the NilFlow* are pointers into - // a single contiguous array. We will need to add an explicit - // numbering when the code is converted to Go. - if(last != nil && r <= last) + if(last != nil && f->id <= last->id) return; - last = r; + last = f; } } diff --git a/src/cmd/6g/opt.h b/src/cmd/gc/popt.h index 11befb6ad1..0a30e81f55 100644 --- a/src/cmd/6g/opt.h +++ b/src/cmd/gc/popt.h @@ -28,38 +28,28 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - #define Z N #define Adr Addr -#define D_HI TYPE_NONE -#define D_LO TYPE_NONE - #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]) #define STORE(r) (~r->calbehind.b[z] & r->calahead.b[z]) -#define CLOAD 5 -#define CREF 5 -#define CINF 1000 -#define LOOP 3 - -typedef struct Reg Reg; -typedef struct Rgn Rgn; - -/*c2go -extern Node *Z; enum { - D_HI = TYPE_NONE, - D_LO = TYPE_NONE, CLOAD = 5, CREF = 5, CINF = 1000, LOOP = 3, }; +typedef struct Reg Reg; +typedef struct Rgn Rgn; + +/*c2go +extern Node *Z; + uint32 BLOAD(Reg*); uint32 BSTORE(Reg*); uint64 LOAD(Reg*); @@ -69,11 +59,8 @@ uint64 STORE(Reg*); // 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->opt points back to r. struct Reg { - Flow f; - Bits set; // regopt variables written by this instruction. Bits use1; // regopt variables read by prog->from. Bits use2; // regopt variables read by prog->to. @@ -91,7 +78,7 @@ struct Reg Bits regdiff; Bits act; - int32 regu; // register used bitmap + uint64 regu; // register used bitmap }; #define R ((Reg*)0) /*c2go extern Reg *R; */ @@ -110,21 +97,18 @@ struct Reg // cost. struct Rgn { - Reg* enter; + Flow* enter; short cost; short varno; short regno; }; -EXTERN int32 exregoffset; // not set -EXTERN int32 exfregoffset; // not set EXTERN Reg zreg; EXTERN Rgn region[NRGN]; EXTERN Rgn* rgp; EXTERN int nregion; EXTERN int nvar; -EXTERN int32 regbits; -EXTERN int32 exregbits; +EXTERN uint64 regbits; EXTERN Bits externs; EXTERN Bits params; EXTERN Bits consts; @@ -147,34 +131,19 @@ EXTERN struct /* * reg.c */ -int rcmp(const void*, const void*); void regopt(Prog*); -void addmove(Reg*, int, int, int); -Bits mkvar(Reg*, Adr*); -void prop(Reg*, Bits, Bits); -void synch(Reg*, Bits); -uint32 allreg(uint32, Rgn*); -void paint1(Reg*, int); -uint32 paint2(Reg*, int, int); -void paint3(Reg*, int, uint32, int); -void addreg(Adr*, int); void dumpone(Flow*, int); void dumpit(char*, Flow*, int); /* * peep.c - */ void peep(Prog*); void excise(Flow*); int copyu(Prog*, Adr*, Adr*); - -uint32 RtoB(int); -uint32 FtoB(int); -int BtoR(uint32); -int BtoF(uint32); + */ /* * prog.c - */ void proginfo(ProgInfo*, Prog*); + */ diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c index f3134dab23..3aa7e36386 100644 --- a/src/cmd/gc/racewalk.c +++ b/src/cmd/gc/racewalk.c @@ -24,7 +24,6 @@ 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 void makeaddable(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 void appendinit(Node **np, NodeList *init); @@ -155,12 +154,8 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) default: fatal("racewalk: unknown node type %O", n->op); - case OASOP: case OAS: - case OAS2: - case OAS2RECV: case OAS2FUNC: - case OAS2MAPR: racewalknode(&n->left, init, 1, 0); racewalknode(&n->right, init, 0, 0); goto ret; @@ -210,6 +205,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) case OCALLFUNC: // Instrument dst argument of runtime.writebarrier* calls // as we do not instrument runtime code. + // typedslicecopy is instrumented in runtime. if(n->left->sym != S && n->left->sym->pkg == runtimepkg && (strncmp(n->left->sym->name, "writebarrier", 12) == 0 || strcmp(n->left->sym->name, "typedmemmove") == 0)) { // Find the dst argument. @@ -350,7 +346,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) goto ret; case OEFACE: - racewalknode(&n->left, init, 0, 0); + // n->left is Type* which is not interesting. racewalknode(&n->right, init, 0, 0); goto ret; @@ -393,6 +389,10 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) case OARRAYLIT: // lowered to assignments case OMAPLIT: case OSTRUCTLIT: + case OAS2: + case OAS2RECV: + case OAS2MAPR: + case OASOP: yyerror("racewalk: %O must be lowered by now", n->op); goto ret; @@ -489,7 +489,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip) if(isartificial(n)) return 0; - b = basenod(n); + b = outervalue(n); // it skips e.g. stores to ... parameter array if(isartificial(b)) return 0; @@ -499,7 +499,7 @@ callinstr(Node **np, NodeList **init, int wr, int skip) // 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->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) { + || b->op == OINDEX || b->op == ODOTPTR || b->op == OIND) { hascalls = 0; foreach(n, hascallspred, &hascalls); if(hascalls) { @@ -568,25 +568,6 @@ uintptraddr(Node *n) return r; } -// basenod returns the simplest child node of n pointing to the same -// memory area. -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 && isfixedarray(n->type)) { - n = n->left; - continue; - } - break; - } - return n; -} - static Node* detachexpr(Node *n, NodeList **init) { diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index ff05820b58..ff9de6c349 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -13,6 +13,7 @@ void typecheckrange(Node *n) { + int toomany; char *why; Type *t, *t1, *t2; Node *v1, *v2; @@ -41,6 +42,7 @@ typecheckrange(Node *n) t = t->type; n->type = t; + toomany = 0; switch(t->etype) { default: yyerror("cannot range over %lN", n->right); @@ -64,7 +66,7 @@ typecheckrange(Node *n) t1 = t->type; t2 = nil; if(count(n->list) == 2) - goto toomany; + toomany = 1; break; case TSTRING: @@ -73,10 +75,8 @@ typecheckrange(Node *n) break; } - if(count(n->list) > 2) { - toomany: + if(count(n->list) > 2 || toomany) yyerror("too many variables in range"); - } v1 = N; if(n->list) diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 61a63c0528..14a1f13e33 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -117,16 +117,29 @@ enum { }; static Type* +makefield(char *name, Type *t) +{ + Type *f; + + f = typ(TFIELD); + f->type = t; + f->sym = mal(sizeof(Sym)); + f->sym->name = name; + return f; +} + +Type* mapbucket(Type *t) { Type *keytype, *valtype; - Type *bucket; - Type *overflowfield, *keysfield, *valuesfield; - int32 offset; + Type *bucket, *arr; + Type *field[4]; + int32 n; if(t->bucket != T) return t->bucket; + bucket = typ(TSTRUCT); keytype = t->down; valtype = t->type; dowidth(keytype); @@ -136,119 +149,74 @@ mapbucket(Type *t) if(valtype->width > MAXVALSIZE) valtype = ptrto(valtype); - bucket = typ(TSTRUCT); - bucket->noalg = 1; - // The first field is: uint8 topbits[BUCKETSIZE]. - // We don't need to encode it as GC doesn't care about it. - offset = BUCKETSIZE * 1; - - keysfield = typ(TFIELD); - keysfield->type = typ(TARRAY); - keysfield->type->type = keytype; - keysfield->type->bound = BUCKETSIZE; - keysfield->type->width = BUCKETSIZE * keytype->width; - keysfield->width = offset; - keysfield->sym = mal(sizeof(Sym)); - keysfield->sym->name = "keys"; - offset += BUCKETSIZE * keytype->width; + arr = typ(TARRAY); + arr->type = types[TUINT8]; + arr->bound = BUCKETSIZE; + field[0] = makefield("topbits", arr); + arr = typ(TARRAY); + arr->type = keytype; + arr->bound = BUCKETSIZE; + field[1] = makefield("keys", arr); + arr = typ(TARRAY); + arr->type = valtype; + arr->bound = BUCKETSIZE; + field[2] = makefield("values", arr); + field[3] = makefield("overflow", ptrto(bucket)); - valuesfield = typ(TFIELD); - valuesfield->type = typ(TARRAY); - valuesfield->type->type = valtype; - valuesfield->type->bound = BUCKETSIZE; - valuesfield->type->width = BUCKETSIZE * valtype->width; - valuesfield->width = offset; - valuesfield->sym = mal(sizeof(Sym)); - valuesfield->sym->name = "values"; - offset += BUCKETSIZE * valtype->width; + // link up fields + bucket->noalg = 1; + bucket->local = t->local; + bucket->type = field[0]; + for(n = 0; n < nelem(field)-1; n++) + field[n]->down = field[n+1]; + field[nelem(field)-1]->down = T; + dowidth(bucket); - overflowfield = typ(TFIELD); - overflowfield->type = ptrto(bucket); - overflowfield->width = offset; // "width" is offset in structure - overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name - overflowfield->sym->name = "overflow"; - offset += widthptr; - // Pad to the native integer alignment. - // This is usually the same as widthptr; the exception (as usual) is nacl/amd64. + // This is usually the same as widthptr; the exception (as usual) is amd64p32. if(widthreg > widthptr) - offset += widthreg - widthptr; - - // link up fields - bucket->type = keysfield; - keysfield->down = valuesfield; - valuesfield->down = overflowfield; - overflowfield->down = T; + bucket->width += widthreg - widthptr; // See comment on hmap.overflow in ../../runtime/hashmap.go. if(!haspointers(t->type) && !haspointers(t->down)) bucket->haspointers = 1; // no pointers - bucket->width = offset; - bucket->local = t->local; t->bucket = bucket; bucket->map = t; return bucket; } -// Builds a type respresenting a Hmap structure for -// the given map type. This type is not visible to users - -// we include only enough information to generate a correct GC -// program for it. +// Builds a type representing a Hmap structure for the given map type. // Make sure this stays in sync with ../../runtime/hashmap.go! -static Type* +Type* hmap(Type *t) { Type *h, *bucket; - Type *bucketsfield, *oldbucketsfield, *overflowfield; - int32 offset; + Type *field[8]; + int32 n; if(t->hmap != T) return t->hmap; bucket = mapbucket(t); + field[0] = makefield("count", types[TINT]); + field[1] = makefield("flags", types[TUINT8]); + field[2] = makefield("B", types[TUINT8]); + field[3] = makefield("hash0", types[TUINT32]); + field[4] = makefield("buckets", ptrto(bucket)); + field[5] = makefield("oldbuckets", ptrto(bucket)); + field[6] = makefield("nevacuate", types[TUINTPTR]); + field[7] = makefield("overflow", types[TUNSAFEPTR]); + h = typ(TSTRUCT); h->noalg = 1; - - offset = widthint; // count - offset += 1; // flags - offset += 1; // B - offset += 2; // padding - offset += 4; // hash0 - offset = (offset + widthptr - 1) / widthptr * widthptr; - - bucketsfield = typ(TFIELD); - bucketsfield->type = ptrto(bucket); - bucketsfield->width = offset; - bucketsfield->sym = mal(sizeof(Sym)); - bucketsfield->sym->name = "buckets"; - offset += widthptr; - - oldbucketsfield = typ(TFIELD); - oldbucketsfield->type = ptrto(bucket); - oldbucketsfield->width = offset; - oldbucketsfield->sym = mal(sizeof(Sym)); - oldbucketsfield->sym->name = "oldbuckets"; - offset += widthptr; - - offset += widthptr; // nevacuate - - overflowfield = typ(TFIELD); - overflowfield->type = types[TUNSAFEPTR]; - overflowfield->width = offset; - overflowfield->sym = mal(sizeof(Sym)); - overflowfield->sym->name = "overflow"; - offset += widthptr; - - // link up fields - h->type = bucketsfield; - bucketsfield->down = oldbucketsfield; - oldbucketsfield->down = overflowfield; - overflowfield->down = T; - - h->width = offset; h->local = t->local; + h->type = field[0]; + for(n = 0; n < nelem(field)-1; n++) + field[n]->down = field[n+1]; + field[nelem(field)-1]->down = T; + dowidth(h); t->hmap = h; h->map = t; return h; @@ -257,8 +225,8 @@ hmap(Type *t) Type* hiter(Type *t) { - int32 n, off; - Type *field[9]; + int32 n; + Type *field[12]; Type *i; if(t->hiter != T) @@ -272,73 +240,37 @@ hiter(Type *t) // h *Hmap // buckets *Bucket // bptr *Bucket - // overflow unsafe.Pointer - // other [4]uintptr + // overflow0 unsafe.Pointer + // overflow1 unsafe.Pointer + // startBucket uintptr + // stuff uintptr + // bucket uintptr + // checkBucket uintptr // } // must match ../../runtime/hashmap.c:hash_iter. - field[0] = typ(TFIELD); - field[0]->type = ptrto(t->down); - field[0]->sym = mal(sizeof(Sym)); - field[0]->sym->name = "key"; - - field[1] = typ(TFIELD); - field[1]->type = ptrto(t->type); - field[1]->sym = mal(sizeof(Sym)); - field[1]->sym->name = "val"; - - field[2] = typ(TFIELD); - field[2]->type = ptrto(types[TUINT8]); // TODO: is there a Type type? - field[2]->sym = mal(sizeof(Sym)); - field[2]->sym->name = "t"; - - field[3] = typ(TFIELD); - field[3]->type = ptrto(hmap(t)); - field[3]->sym = mal(sizeof(Sym)); - field[3]->sym->name = "h"; - - field[4] = typ(TFIELD); - field[4]->type = ptrto(mapbucket(t)); - field[4]->sym = mal(sizeof(Sym)); - field[4]->sym->name = "buckets"; - - field[5] = typ(TFIELD); - field[5]->type = ptrto(mapbucket(t)); - field[5]->sym = mal(sizeof(Sym)); - field[5]->sym->name = "bptr"; - - field[6] = typ(TFIELD); - field[6]->type = types[TUNSAFEPTR]; - field[6]->sym = mal(sizeof(Sym)); - field[6]->sym->name = "overflow0"; - - field[7] = typ(TFIELD); - field[7]->type = types[TUNSAFEPTR]; - field[7]->sym = mal(sizeof(Sym)); - field[7]->sym->name = "overflow1"; - - // all other non-pointer fields - field[8] = typ(TFIELD); - field[8]->type = typ(TARRAY); - field[8]->type->type = types[TUINTPTR]; - field[8]->type->bound = 4; - field[8]->type->width = 4 * widthptr; - field[8]->sym = mal(sizeof(Sym)); - field[8]->sym->name = "other"; + field[0] = makefield("key", ptrto(t->down)); + field[1] = makefield("val", ptrto(t->type)); + field[2] = makefield("t", ptrto(types[TUINT8])); + field[3] = makefield("h", ptrto(hmap(t))); + field[4] = makefield("buckets", ptrto(mapbucket(t))); + field[5] = makefield("bptr", ptrto(mapbucket(t))); + field[6] = makefield("overflow0", types[TUNSAFEPTR]); + field[7] = makefield("overflow1", types[TUNSAFEPTR]); + field[8] = makefield("startBucket", types[TUINTPTR]); + field[9] = makefield("stuff", types[TUINTPTR]); // offset+wrapped+B+I + field[10] = makefield("bucket", types[TUINTPTR]); + field[11] = makefield("checkBucket", types[TUINTPTR]); // build iterator struct holding the above fields i = typ(TSTRUCT); i->noalg = 1; i->type = field[0]; - off = 0; - for(n = 0; n < nelem(field)-1; n++) { + for(n = 0; n < nelem(field)-1; n++) field[n]->down = field[n+1]; - field[n]->width = off; - off += field[n]->type->width; - } field[nelem(field)-1]->down = T; - off += field[nelem(field)-1]->type->width; - if(off != 12 * widthptr) - yyerror("hash_iter size not correct %d %d", off, 11 * widthptr); + dowidth(i); + if(i->width != 12 * widthptr) + yyerror("hash_iter size not correct %d %d", i->width, 12 * widthptr); t->hiter = i; i->map = t; return i; @@ -547,15 +479,15 @@ dimportpath(Pkg *p) n->xoffset = 0; p->pathsym = n->sym; - arch.gdatastring(n, p->path); - arch.ggloblsym(n->sym, types[TSTRING]->width, DUPOK|RODATA); + gdatastring(n, p->path); + ggloblsym(n->sym, types[TSTRING]->width, DUPOK|RODATA); } static int dgopkgpath(Sym *s, int ot, Pkg *pkg) { if(pkg == nil) - return arch.dgostringptr(s, ot, nil); + return dgostringptr(s, ot, nil); // Emit reference to go.importpath.""., which 6l will // rewrite using the correct import path. Every package @@ -565,11 +497,11 @@ dgopkgpath(Sym *s, int ot, Pkg *pkg) if(ns == nil) ns = pkglookup("importpath.\"\".", mkpkg(newstrlit("go"))); - return arch.dsymptr(s, ot, ns, 0); + return dsymptr(s, ot, ns, 0); } dimportpath(pkg); - return arch.dsymptr(s, ot, pkg->pathsym, 0); + return dsymptr(s, ot, pkg->pathsym, 0); } /* @@ -589,7 +521,7 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) // fill in *extraType pointer in header off = rnd(off, widthptr); - arch.dsymptr(sym, ptroff, sym, off); + dsymptr(sym, ptroff, sym, off); n = 0; for(a=m; a; a=a->link) { @@ -600,18 +532,18 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) ot = off; s = sym; if(t->sym) { - ot = arch.dgostringptr(s, ot, t->sym->name); + ot = dgostringptr(s, ot, t->sym->name); if(t != types[t->etype] && t != errortype) ot = dgopkgpath(s, ot, t->sym->pkg); else - ot = arch.dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); } else { - ot = arch.dgostringptr(s, ot, nil); - ot = arch.dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); } // slice header - ot = arch.dsymptr(s, ot, s, ot + widthptr + 2*widthint); + ot = dsymptr(s, ot, s, ot + widthptr + 2*widthint); ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint); @@ -619,16 +551,16 @@ dextratype(Sym *sym, int off, Type *t, int ptroff) for(a=m; a; a=a->link) { // method // ../../runtime/type.go:/method - ot = arch.dgostringptr(s, ot, a->name); + ot = dgostringptr(s, ot, a->name); ot = dgopkgpath(s, ot, a->pkg); - ot = arch.dsymptr(s, ot, dtypesym(a->mtype), 0); - ot = arch.dsymptr(s, ot, dtypesym(a->type), 0); + ot = dsymptr(s, ot, dtypesym(a->mtype), 0); + ot = dsymptr(s, ot, dtypesym(a->type), 0); if(a->isym) - ot = arch.dsymptr(s, ot, a->isym, 0); + ot = dsymptr(s, ot, a->isym, 0); else ot = duintptr(s, ot, 0); if(a->tsym) - ot = arch.dsymptr(s, ot, a->tsym, 0); + ot = dsymptr(s, ot, a->tsym, 0); else ot = duintptr(s, ot, 0); } @@ -816,43 +748,43 @@ dcommontype(Sym *s, int ot, Type *t) i |= KindGCProg; ot = duint8(s, ot, i); // kind if(algsym == S) - ot = arch.dsymptr(s, ot, algarray, alg*sizeofAlg); + ot = dsymptr(s, ot, algarray, alg*sizeofAlg); else - ot = arch.dsymptr(s, ot, algsym, 0); + ot = dsymptr(s, ot, algsym, 0); // gc if(gcprog) { gengcprog(t, &gcprog0, &gcprog1); if(gcprog0 != S) - ot = arch.dsymptr(s, ot, gcprog0, 0); + ot = dsymptr(s, ot, gcprog0, 0); else ot = duintptr(s, ot, 0); - ot = arch.dsymptr(s, ot, gcprog1, 0); + ot = dsymptr(s, ot, gcprog1, 0); } else { gengcmask(t, gcmask); x1 = 0; for(i=0; i<8; i++) x1 = x1<<8 | gcmask[i]; if(widthptr == 4) { - p = smprint("gcbits.%#016llux", x1); + p = smprint("gcbits.0x%016llux", x1); } else { x2 = 0; for(i=0; i<8; i++) x2 = x2<<8 | gcmask[i+8]; - p = smprint("gcbits.%#016llux%016llux", x1, x2); + p = smprint("gcbits.0x%016llux%016llux", x1, x2); } sbits = pkglookup(p, runtimepkg); if((sbits->flags & SymUniq) == 0) { sbits->flags |= SymUniq; for(i = 0; i < 2*widthptr; i++) duint8(sbits, i, gcmask[i]); - arch.ggloblsym(sbits, 2*widthptr, DUPOK|RODATA); + ggloblsym(sbits, 2*widthptr, DUPOK|RODATA); } - ot = arch.dsymptr(s, ot, sbits, 0); + ot = dsymptr(s, ot, sbits, 0); ot = duintptr(s, ot, 0); } p = smprint("%-uT", t); //print("dcommontype: %s\n", p); - ot = arch.dgostringptr(s, ot, p); // string + ot = dgostringptr(s, ot, p); // string free(p); // skip pointer to extraType, @@ -861,8 +793,8 @@ dcommontype(Sym *s, int ot, Type *t) // otherwise linker will assume 0. ot += widthptr; - ot = arch.dsymptr(s, ot, sptr, 0); // ptrto type - ot = arch.dsymptr(s, ot, zero, 0); // ptr to zero value + ot = dsymptr(s, ot, sptr, 0); // ptrto type + ot = dsymptr(s, ot, zero, 0); // ptr to zero value return ot; } @@ -1092,15 +1024,15 @@ ok: s2 = dtypesym(t2); ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s1, 0); - ot = arch.dsymptr(s, ot, s2, 0); + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); ot = duintptr(s, ot, t->bound); } else { // ../../runtime/type.go:/SliceType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s1, 0); } break; @@ -1109,7 +1041,7 @@ ok: s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s1, 0); ot = duintptr(s, ot, t->chan); break; @@ -1130,21 +1062,21 @@ ok: // two slice headers: in and out. ot = rnd(ot, widthptr); - ot = arch.dsymptr(s, ot, s, ot+2*(widthptr+2*widthint)); + ot = dsymptr(s, ot, s, ot+2*(widthptr+2*widthint)); n = t->thistuple + t->intuple; ot = duintxx(s, ot, n, widthint); ot = duintxx(s, ot, n, widthint); - ot = arch.dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr); + 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++) - ot = arch.dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dsymptr(s, ot, dtypesym(t1->type), 0); for(t1=getinargx(t)->type; t1; t1=t1->down, n++) - ot = arch.dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dsymptr(s, ot, dtypesym(t1->type), 0); for(t1=getoutargx(t)->type; t1; t1=t1->down, n++) - ot = arch.dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dsymptr(s, ot, dtypesym(t1->type), 0); break; case TINTER: @@ -1158,14 +1090,14 @@ ok: // ../../runtime/type.go:/InterfaceType ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s, ot+widthptr+2*widthint); + 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) { // ../../runtime/type.go:/imethod - ot = arch.dgostringptr(s, ot, a->name); + ot = dgostringptr(s, ot, a->name); ot = dgopkgpath(s, ot, a->pkg); - ot = arch.dsymptr(s, ot, dtypesym(a->type), 0); + ot = dsymptr(s, ot, dtypesym(a->type), 0); } break; @@ -1177,10 +1109,10 @@ ok: s4 = dtypesym(hmap(t)); ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s1, 0); - ot = arch.dsymptr(s, ot, s2, 0); - ot = arch.dsymptr(s, ot, s3, 0); - ot = arch.dsymptr(s, ot, s4, 0); + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); + ot = dsymptr(s, ot, s3, 0); + ot = dsymptr(s, ot, s4, 0); if(t->down->width > MAXKEYSIZE) { ot = duint8(s, ot, widthptr); ot = duint8(s, ot, 1); // indirect @@ -1210,7 +1142,7 @@ ok: s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s1, 0); break; case TSTRUCT: @@ -1223,32 +1155,32 @@ ok: } ot = dcommontype(s, ot, t); xt = ot - 3*widthptr; - ot = arch.dsymptr(s, ot, s, ot+widthptr+2*widthint); + 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) { // ../../runtime/type.go:/structField if(t1->sym && !t1->embedded) { - ot = arch.dgostringptr(s, ot, t1->sym->name); + ot = dgostringptr(s, ot, t1->sym->name); if(exportname(t1->sym->name)) - ot = arch.dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); else ot = dgopkgpath(s, ot, t1->sym->pkg); } else { - ot = arch.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 = arch.dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); } - ot = arch.dsymptr(s, ot, dtypesym(t1->type), 0); - ot = arch.dgostrlitptr(s, ot, t1->note); + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dgostrlitptr(s, ot, t1->note); ot = duintptr(s, ot, t1->width); // field offset } break; } ot = dextratype(s, ot, t, xt); - arch.ggloblsym(s, ot, dupok|RODATA); + ggloblsym(s, ot, dupok|RODATA); // generate typelink.foo pointing at s = type.foo. // The linker will leave a table of all the typelinks for @@ -1261,8 +1193,8 @@ ok: case TCHAN: case TMAP: slink = typelinksym(t); - arch.dsymptr(slink, 0, s, 0); - arch.ggloblsym(slink, widthptr, dupok|RODATA); + dsymptr(slink, 0, s, 0); + ggloblsym(slink, widthptr, dupok|RODATA); } } @@ -1354,18 +1286,18 @@ dalgsym(Type *t) hashfunc = pkglookup(p, typepkg); free(p); ot = 0; - ot = arch.dsymptr(hashfunc, ot, pkglookup("memhash_varlen", runtimepkg), 0); + ot = dsymptr(hashfunc, ot, pkglookup("memhash_varlen", runtimepkg), 0); ot = duintxx(hashfunc, ot, t->width, widthptr); // size encoded in closure - arch.ggloblsym(hashfunc, ot, DUPOK|RODATA); + ggloblsym(hashfunc, ot, DUPOK|RODATA); // make equality closure p = smprint(".eqfunc%lld", t->width); eqfunc = pkglookup(p, typepkg); free(p); ot = 0; - ot = arch.dsymptr(eqfunc, ot, pkglookup("memequal_varlen", runtimepkg), 0); + ot = dsymptr(eqfunc, ot, pkglookup("memequal_varlen", runtimepkg), 0); ot = duintxx(eqfunc, ot, t->width, widthptr); - arch.ggloblsym(eqfunc, ot, DUPOK|RODATA); + ggloblsym(eqfunc, ot, DUPOK|RODATA); } else { // generate an alg table specific to this type s = typesymprefix(".alg", t); @@ -1378,16 +1310,16 @@ dalgsym(Type *t) geneq(eq, t); // make Go funcs (closures) for calling hash and equal from Go - arch.dsymptr(hashfunc, 0, hash, 0); - arch.ggloblsym(hashfunc, widthptr, DUPOK|RODATA); - arch.dsymptr(eqfunc, 0, eq, 0); - arch.ggloblsym(eqfunc, widthptr, DUPOK|RODATA); + dsymptr(hashfunc, 0, hash, 0); + ggloblsym(hashfunc, widthptr, DUPOK|RODATA); + dsymptr(eqfunc, 0, eq, 0); + ggloblsym(eqfunc, widthptr, DUPOK|RODATA); } // ../../runtime/alg.go:/typeAlg ot = 0; - ot = arch.dsymptr(s, ot, hashfunc, 0); - ot = arch.dsymptr(s, ot, eqfunc, 0); - arch.ggloblsym(s, ot, DUPOK|RODATA); + ot = dsymptr(s, ot, hashfunc, 0); + ot = dsymptr(s, ot, eqfunc, 0); + ggloblsym(s, ot, DUPOK|RODATA); return s; } @@ -1417,7 +1349,7 @@ usegcprog(Type *t) // Generates sparse GC bitmask (4 bits per word). static void -gengcmask(Type *t, uint8 gcmask[16]) +gengcmask(Type *t, uint8 *gcmask) { Bvec *vec; vlong xoffset, nptr, i, j; @@ -1570,7 +1502,7 @@ gengcprog(Type *t, Sym **pgc0, Sym **pgc1) // Don't generate it if it's too large, runtime will unroll directly into GC bitmap. if(size <= MaxGCMask) { gc0 = typesymprefix(".gc", t); - arch.ggloblsym(gc0, size, DUPOK|NOPTR); + ggloblsym(gc0, size, DUPOK|NOPTR); *pgc0 = gc0; } @@ -1580,7 +1512,7 @@ gengcprog(Type *t, Sym **pgc0, Sym **pgc1) xoffset = 0; gengcprog1(&g, t, &xoffset); ot = proggenfini(&g); - arch.ggloblsym(gc1, ot, DUPOK|RODATA); + ggloblsym(gc1, ot, DUPOK|RODATA); *pgc1 = gc1; } diff --git a/src/cmd/gc/reg.c b/src/cmd/gc/reg.c new file mode 100644 index 0000000000..67409c2127 --- /dev/null +++ b/src/cmd/gc/reg.c @@ -0,0 +1,1233 @@ +// Derived from Inferno utils/6c/reg.c +// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 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. + +#include <u.h> +#include <libc.h> +#include "go.h" +#include "popt.h" + +static Flow* firstf; +static int first = 1; + +static void addmove(Flow*, int, int, int); +static Bits mkvar(Flow*, Adr*); +static void prop(Flow*, Bits, Bits); +static void synch(Flow*, Bits); +static uint64 allreg(uint64, Rgn*); +static void paint1(Flow*, int); +static uint64 paint2(Flow*, int, int); +static void paint3(Flow*, int, uint64, int); +static void addreg(Adr*, int); + +static int +rcmp(const void *a1, const void *a2) +{ + Rgn *p1, *p2; + + p1 = (Rgn*)a1; + p2 = (Rgn*)a2; + if(p1->cost != p2->cost) + return p2->cost - p1->cost; + if(p1->varno != p2->varno) + return p2->varno - p1->varno; + if(p1->enter != p2->enter) + return p2->enter->id - p1->enter->id; + return 0; +} + +static void +setaddrs(Bits bit) +{ + int i, n; + Var *v; + Node *node; + + while(bany(&bit)) { + // convert each bit to a variable + i = bnum(bit); + node = var[i].node; + n = var[i].name; + biclr(&bit, i); + + // disable all pieces of that variable + for(i=0; i<nvar; i++) { + v = var+i; + if(v->node == node && v->name == n) + v->addr = 2; + } + } +} + +static Node* regnodes[64]; + +static void walkvardef(Node *n, Flow *r, int active); + +void +regopt(Prog *firstp) +{ + Flow *f, *f1; + Reg *r; + Prog *p; + Graph *g; + ProgInfo info; + int i, z, active; + uint64 vreg, usedreg; + uint64 mask; + int nreg; + char **regnames; + Bits bit; + Rgn *rgp; + + if(first) { + fmtinstall('Q', Qconv); + first = 0; + } + + mergetemp(firstp); + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + regnames = thearch.regnames(&nreg); + nvar = nreg; + memset(var, 0, nreg*sizeof var[0]); + for(i=0; i<nreg; i++) { + if(regnodes[i] == N) + regnodes[i] = newname(lookup(regnames[i])); + var[i].node = regnodes[i]; + } + + regbits = thearch.excludedregs(); + externs = zbits; + params = zbits; + consts = zbits; + addrs = zbits; + ivar = zbits; + ovar = zbits; + + /* + * pass 1 + * build aux data structure + * allocate pcs + * find use and set of variables + */ + g = flowstart(firstp, sizeof(Reg)); + if(g == nil) { + for(i=0; i<nvar; i++) + var[i].node->opt = nil; + return; + } + + firstf = g->start; + + for(f = firstf; f != nil; f = f->link) { + p = f->prog; + if(p->as == AVARDEF || p->as == AVARKILL) + continue; + thearch.proginfo(&info, p); + + // Avoid making variables for direct-called functions. + if(p->as == ACALL && p->to.type == TYPE_MEM && p->to.name == NAME_EXTERN) + continue; + + // from vs to doesn't matter for registers. + r = (Reg*)f->data; + r->use1.b[0] |= info.reguse | info.regindex; + r->set.b[0] |= info.regset; + + bit = mkvar(f, &p->from); + if(bany(&bit)) { + if(info.flags & LeftAddr) + setaddrs(bit); + if(info.flags & LeftRead) + for(z=0; z<BITS; z++) + r->use1.b[z] |= bit.b[z]; + if(info.flags & LeftWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; + } + + // Compute used register for reg + if(info.flags & RegRead) + r->use1.b[0] |= thearch.RtoB(p->reg); + + // Currently we never generate three register forms. + // If we do, this will need to change. + if(p->from3.type != TYPE_NONE) + fatal("regopt not implemented for from3"); + + bit = mkvar(f, &p->to); + if(bany(&bit)) { + if(info.flags & RightAddr) + setaddrs(bit); + if(info.flags & RightRead) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + if(info.flags & RightWrite) + for(z=0; z<BITS; z++) + r->set.b[z] |= bit.b[z]; + } + } + + for(i=0; i<nvar; i++) { + Var *v; + v = var+i; + if(v->addr) { + bit = blsh(i); + for(z=0; z<BITS; z++) + addrs.b[z] |= bit.b[z]; + } + + if(debug['R'] && debug['v']) + print("bit=%2d addr=%d et=%E w=%-2d s=%N + %lld\n", + i, v->addr, v->etype, v->width, v->node, v->offset); + } + + if(debug['R'] && debug['v']) + dumpit("pass1", firstf, 1); + + /* + * pass 2 + * find looping structure + */ + flowrpo(g); + + if(debug['R'] && debug['v']) + dumpit("pass2", firstf, 1); + + /* + * pass 2.5 + * iterate propagating fat vardef covering forward + * r->act records vars with a VARDEF since the last CALL. + * (r->act will be reused in pass 5 for something else, + * but we'll be done with it by then.) + */ + active = 0; + for(f = firstf; f != nil; f = f->link) { + f->active = 0; + r = (Reg*)f->data; + r->act = zbits; + } + for(f = firstf; f != nil; f = f->link) { + p = f->prog; + if(p->as == AVARDEF && isfat(((Node*)(p->to.node))->type) && ((Node*)(p->to.node))->opt != nil) { + active++; + walkvardef(p->to.node, f, active); + } + } + + /* + * pass 3 + * iterate propagating usage + * back until flow graph is complete + */ +loop1: + change = 0; + for(f = firstf; f != nil; f = f->link) + f->active = 0; + for(f = firstf; f != nil; f = f->link) + if(f->prog->as == ARET) + prop(f, zbits, zbits); +loop11: + /* pick up unreachable code */ + i = 0; + for(f = firstf; f != nil; f = f1) { + f1 = f->link; + if(f1 && f1->active && !f->active) { + prop(f, zbits, zbits); + i = 1; + } + } + if(i) + goto loop11; + if(change) + goto loop1; + + if(debug['R'] && debug['v']) + dumpit("pass3", firstf, 1); + + /* + * pass 4 + * iterate propagating register/variable synchrony + * forward until graph is complete + */ +loop2: + change = 0; + for(f = firstf; f != nil; f = f->link) + f->active = 0; + synch(firstf, zbits); + if(change) + goto loop2; + + if(debug['R'] && debug['v']) + dumpit("pass4", firstf, 1); + + /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + if(nreg == 64) + mask = ~0ULL; // can't rely on C to shift by 64 + else + mask = (1ULL<<nreg) - 1; + for(f = firstf; f != nil; f = f->link) { + r = (Reg*)f->data; + r->regu = (r->refbehind.b[0] | r->set.b[0]) & mask; + r->set.b[0] &= ~mask; + r->use1.b[0] &= ~mask; + r->use2.b[0] &= ~mask; + r->refbehind.b[0] &= ~mask; + r->refahead.b[0] &= ~mask; + r->calbehind.b[0] &= ~mask; + r->calahead.b[0] &= ~mask; + r->regdiff.b[0] &= ~mask; + r->act.b[0] &= ~mask; + } + + if(debug['R'] && debug['v']) + dumpit("pass4.5", firstf, 1); + + /* + * pass 5 + * isolate regions + * calculate costs (paint1) + */ + f = firstf; + if(f) { + r = (Reg*)f->data; + for(z=0; z<BITS; z++) + bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & + ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); + if(bany(&bit) && !f->refset) { + // should never happen - all variables are preset + if(debug['w']) + print("%L: used and not set: %Q\n", f->prog->lineno, bit); + f->refset = 1; + } + } + for(f = firstf; f != nil; f = f->link) + ((Reg*)f->data)->act = zbits; + nregion = 0; + for(f = firstf; f != nil; f = f->link) { + r = (Reg*)f->data; + for(z=0; z<BITS; z++) + bit.b[z] = r->set.b[z] & + ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); + if(bany(&bit) && !f->refset) { + if(debug['w']) + print("%L: set and not used: %Q\n", f->prog->lineno, bit); + f->refset = 1; + thearch.excise(f); + } + for(z=0; z<BITS; z++) + bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); + while(bany(&bit)) { + i = bnum(bit); + change = 0; + paint1(f, i); + biclr(&bit, i); + if(change <= 0) + continue; + if(nregion >= NRGN) { + if(debug['R'] && debug['v']) + print("too many regions\n"); + goto brk; + } + rgp = ®ion[nregion]; + rgp->enter = f; + rgp->varno = i; + rgp->cost = change; + nregion++; + } + } +brk: + qsort(region, nregion, sizeof(region[0]), rcmp); + + if(debug['R'] && debug['v']) + dumpit("pass5", firstf, 1); + + /* + * pass 6 + * determine used registers (paint2) + * replace code (paint3) + */ + if(debug['R'] && debug['v']) + print("\nregisterizing\n"); + for(i=0; i<nregion; i++) { + rgp = ®ion[i]; + if(debug['R'] && debug['v']) + print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->prog->pc); + bit = blsh(rgp->varno); + usedreg = paint2(rgp->enter, rgp->varno, 0); + vreg = allreg(usedreg, rgp); + if(rgp->regno != 0) { + if(debug['R'] && debug['v']) { + Var *v; + + v = var + rgp->varno; + print("registerize %N+%lld (bit=%2d et=%E) in %R usedreg=%#llx vreg=%#llx\n", + v->node, v->offset, rgp->varno, v->etype, rgp->regno, usedreg, vreg); + } + paint3(rgp->enter, rgp->varno, vreg, rgp->regno); + } + } + + /* + * free aux structures. peep allocates new ones. + */ + for(i=0; i<nvar; i++) + var[i].node->opt = nil; + flowend(g); + firstf = nil; + + if(debug['R'] && debug['v']) { + // Rebuild flow graph, since we inserted instructions + g = flowstart(firstp, 0); + firstf = g->start; + dumpit("pass6", firstf, 0); + flowend(g); + firstf = nil; + } + + /* + * pass 7 + * peep-hole on basic block + */ + if(!debug['R'] || debug['P']) + thearch.peep(firstp); + + /* + * eliminate nops + */ + for(p=firstp; p!=P; p=p->link) { + while(p->link != P && p->link->as == ANOP) + p->link = p->link->link; + if(p->to.type == TYPE_BRANCH) + while(p->to.u.branch != P && p->to.u.branch->as == ANOP) + p->to.u.branch = p->to.u.branch->link; + } + + if(debug['R']) { + if(ostats.ncvtreg || + ostats.nspill || + ostats.nreload || + ostats.ndelmov || + ostats.nvar || + ostats.naddr || + 0) + print("\nstats\n"); + + if(ostats.ncvtreg) + print(" %4d cvtreg\n", ostats.ncvtreg); + if(ostats.nspill) + print(" %4d spill\n", ostats.nspill); + if(ostats.nreload) + print(" %4d reload\n", ostats.nreload); + if(ostats.ndelmov) + print(" %4d delmov\n", ostats.ndelmov); + if(ostats.nvar) + print(" %4d var\n", ostats.nvar); + if(ostats.naddr) + print(" %4d addr\n", ostats.naddr); + + memset(&ostats, 0, sizeof(ostats)); + } +} + +static void +walkvardef(Node *n, Flow *f, int active) +{ + Flow *f1, *f2; + int bn; + Var *v; + + for(f1=f; f1!=nil; f1=f1->s1) { + if(f1->active == active) + break; + f1->active = active; + if(f1->prog->as == AVARKILL && f1->prog->to.node == n) + break; + for(v=n->opt; v!=nil; v=v->nextinnode) { + bn = v->id; + biset(&((Reg*)f1->data)->act, bn); + } + if(f1->prog->as == ACALL) + break; + } + + for(f2=f; f2!=f1; f2=f2->s1) + if(f2->s2 != nil) + walkvardef(n, f2->s2, active); +} + +/* + * add mov b,rn + * just after r + */ +static void +addmove(Flow *r, int bn, int rn, int f) +{ + Prog *p, *p1; + Adr *a; + Var *v; + + p1 = mal(sizeof(*p1)); + clearp(p1); + p1->pc = 9999; + + p = r->prog; + p1->link = p->link; + p->link = p1; + p1->lineno = p->lineno; + + v = var + bn; + + a = &p1->to; + a->offset = v->offset; + a->etype = v->etype; + a->type = TYPE_MEM; + a->name = v->name; + a->node = v->node; + a->sym = linksym(v->node->sym); + /* NOTE(rsc): 9g did + if(a->etype == TARRAY) + a->type = TYPE_ADDR; + else if(a->sym == nil) + a->type = TYPE_CONST; + */ + + p1->as = thearch.optoas(OAS, types[(uchar)v->etype]); + // TODO(rsc): Remove special case here. + if((thearch.thechar == '9' || thearch.thechar == '5') && v->etype == TBOOL) + p1->as = thearch.optoas(OAS, types[TUINT8]); + p1->from.type = TYPE_REG; + p1->from.reg = rn; + p1->from.name = NAME_NONE; + if(!f) { + p1->from = *a; + *a = zprog.from; + a->type = TYPE_REG; + a->reg = rn; + } + if(debug['R'] && debug['v']) + print("%P ===add=== %P\n", p, p1); + ostats.nspill++; +} + +static int +overlap(int64 o1, int w1, int64 o2, int w2) +{ + int64 t1, t2; + + t1 = o1+w1; + t2 = o2+w2; + + if(!(t1 > o2 && t2 > o1)) + return 0; + + return 1; +} + +static Bits +mkvar(Flow *f, Adr *a) +{ + Var *v; + int i, n, et, z, flag; + int64 w; + uint64 regu; + int64 o; + Bits bit; + Node *node; + Reg *r; + + + /* + * mark registers used + */ + if(a->type == TYPE_NONE) + goto none; + + r = (Reg*)f->data; + r->use1.b[0] |= thearch.doregbits(a->index); // TODO: Use RtoB + + switch(a->type) { + default: + regu = thearch.doregbits(a->reg) | thearch.RtoB(a->reg); // TODO: Use RtoB + if(regu == 0) + goto none; + bit = zbits; + bit.b[0] = regu; + return bit; + + case TYPE_ADDR: + // TODO(rsc): Remove special case here. + if(thearch.thechar == '9' || thearch.thechar == '5') + goto memcase; + a->type = TYPE_MEM; + bit = mkvar(f, a); + setaddrs(bit); + a->type = TYPE_ADDR; + ostats.naddr++; + goto none; + + case TYPE_MEM: + memcase: + if(r != R) { + r->use1.b[0] |= thearch.RtoB(a->reg); + /* NOTE: 5g did + if(r->f.prog->scond & (C_PBIT|C_WBIT)) + r->set.b[0] |= RtoB(a->reg); + */ + } + switch(a->name) { + default: + goto none; + case NAME_EXTERN: + case NAME_STATIC: + case NAME_PARAM: + case NAME_AUTO: + n = a->name; + break; + } + } + + node = a->node; + if(node == N || node->op != ONAME || node->orig == N) + goto none; + node = node->orig; + if(node->orig != node) + fatal("%D: bad node", a); + if(node->sym == S || node->sym->name[0] == '.') + goto none; + 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++) { + v = var+i; + if(v->node == node && v->name == n) { + if(v->offset == o) + if(v->etype == et) + if(v->width == w) { + // TODO(rsc): Remove special case for arm here. + if(!flag || thearch.thechar != '5') + return blsh(i); + } + + // if they overlap, disable both + if(overlap(v->offset, v->width, o, w)) { +// print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); + v->addr = 1; + flag = 1; + } + } + } + + switch(et) { + case 0: + case TFUNC: + goto none; + } + + if(nvar >= NVAR) { + if(debug['w'] > 1 && node != N) + fatal("variable not optimized: %#N", node); + + // If we're not tracking a word in a variable, mark the rest as + // having its address taken, so that we keep the whole thing + // live at all calls. otherwise we might optimize away part of + // a variable but not all of it. + for(i=0; i<nvar; i++) { + v = var+i; + if(v->node == node) + v->addr = 1; + } + goto none; + } + + i = nvar; + nvar++; + v = var+i; + v->id = i; + v->offset = o; + v->name = n; + v->etype = et; + v->width = w; + v->addr = flag; // funny punning + v->node = node; + + // node->opt is the head of a linked list + // of Vars within the given Node, so that + // we can start at a Var and find all the other + // Vars in the same Go variable. + v->nextinnode = node->opt; + node->opt = v; + + bit = blsh(i); + if(n == NAME_EXTERN || n == NAME_STATIC) + for(z=0; z<BITS; z++) + externs.b[z] |= bit.b[z]; + if(n == NAME_PARAM) + for(z=0; z<BITS; z++) + params.b[z] |= bit.b[z]; + + if(node->class == PPARAM) + for(z=0; z<BITS; z++) + ivar.b[z] |= bit.b[z]; + if(node->class == PPARAMOUT) + for(z=0; z<BITS; z++) + ovar.b[z] |= bit.b[z]; + + // Treat values with their address taken as live at calls, + // because the garbage collector's liveness analysis in ../gc/plive.c does. + // These must be consistent or else we will elide stores and the garbage + // collector will see uninitialized data. + // The typical case where our own analysis is out of sync is when the + // node appears to have its address taken but that code doesn't actually + // get generated and therefore doesn't show up as an address being + // taken when we analyze the instruction stream. + // One instance of this case is when a closure uses the same name as + // an outer variable for one of its own variables declared with :=. + // The parser flags the outer variable as possibly shared, and therefore + // sets addrtaken, even though it ends up not being actually shared. + // If we were better about _ elision, _ = &x would suffice too. + // The broader := in a closure problem is mentioned in a comment in + // closure.c:/^typecheckclosure and dcl.c:/^oldname. + if(node->addrtaken) + v->addr = 1; + + // Disable registerization for globals, because: + // (1) we might panic at any time and we want the recovery code + // to see the latest values (issue 1304). + // (2) we don't know what pointers might point at them and we want + // loads via those pointers to see updated values and vice versa (issue 7995). + // + // Disable registerization for results if using defer, because the deferred func + // might recover and return, causing the current values to be used. + if(node->class == PEXTERN || (hasdefer && node->class == PPARAMOUT)) + v->addr = 1; + + if(debug['R']) + print("bit=%2d et=%E w=%lld+%lld %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); + ostats.nvar++; + + return bit; + +none: + return zbits; +} + +static void +prop(Flow *f, Bits ref, Bits cal) +{ + Flow *f1, *f2; + Reg *r, *r1; + int z, i; + Var *v, *v1; + + for(f1 = f; f1 != nil; f1 = f1->p1) { + r1 = (Reg*)f1->data; + for(z=0; z<BITS; z++) { + ref.b[z] |= r1->refahead.b[z]; + if(ref.b[z] != r1->refahead.b[z]) { + r1->refahead.b[z] = ref.b[z]; + change++; + } + cal.b[z] |= r1->calahead.b[z]; + if(cal.b[z] != r1->calahead.b[z]) { + r1->calahead.b[z] = cal.b[z]; + change++; + } + } + switch(f1->prog->as) { + case ACALL: + if(noreturn(f1->prog)) + break; + + // Mark all input variables (ivar) as used, because that's what the + // liveness bitmaps say. The liveness bitmaps say that so that a + // panic will not show stale values in the parameter dump. + // Mark variables with a recent VARDEF (r1->act) as used, + // so that the optimizer flushes initializations to memory, + // so that if a garbage collection happens during this CALL, + // the collector will see initialized memory. Again this is to + // match what the liveness bitmaps say. + for(z=0; z<BITS; z++) { + cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1->act.b[z]; + ref.b[z] = 0; + } + + // cal.b is the current approximation of what's live across the call. + // Every bit in cal.b is a single stack word. For each such word, + // find all the other tracked stack words in the same Go variable + // (struct/slice/string/interface) and mark them live too. + // This is necessary because the liveness analysis for the garbage + // collector works at variable granularity, not at word granularity. + // It is fundamental for slice/string/interface: the garbage collector + // needs the whole value, not just some of the words, in order to + // interpret the other bits correctly. Specifically, slice needs a consistent + // ptr and cap, string needs a consistent ptr and len, and interface + // needs a consistent type word and data word. + for(z=0; z<BITS; z++) { + if(cal.b[z] == 0) + continue; + for(i=0; i<64; i++) { + if(z*64+i >= nvar || ((cal.b[z]>>i)&1) == 0) + continue; + v = var+z*64+i; + if(v->node->opt == nil) // v represents fixed register, not Go variable + continue; + + // v->node->opt is the head of a linked list of Vars + // corresponding to tracked words from the Go variable v->node. + // Walk the list and set all the bits. + // For a large struct this could end up being quadratic: + // after the first setting, the outer loop (for z, i) would see a 1 bit + // for all of the remaining words in the struct, and for each such + // word would go through and turn on all the bits again. + // To avoid the quadratic behavior, we only turn on the bits if + // v is the head of the list or if the head's bit is not yet turned on. + // This will set the bits at most twice, keeping the overall loop linear. + v1 = v->node->opt; + if(v == v1 || !btest(&cal, v1->id)) { + for(; v1 != nil; v1 = v1->nextinnode) { + biset(&cal, v1->id); + } + } + } + } + break; + + case ATEXT: + for(z=0; z<BITS; z++) { + cal.b[z] = 0; + ref.b[z] = 0; + } + break; + + case ARET: + for(z=0; z<BITS; z++) { + cal.b[z] = externs.b[z] | ovar.b[z]; + ref.b[z] = 0; + } + break; + } + for(z=0; z<BITS; z++) { + ref.b[z] = (ref.b[z] & ~r1->set.b[z]) | + r1->use1.b[z] | r1->use2.b[z]; + cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); + r1->refbehind.b[z] = ref.b[z]; + r1->calbehind.b[z] = cal.b[z]; + } + if(f1->active) + break; + f1->active = 1; + } + + for(; f != f1; f = f->p1) { + r = (Reg*)f->data; + for(f2 = f->p2; f2 != nil; f2 = f2->p2link) + prop(f2, r->refbehind, r->calbehind); + } +} + +static void +synch(Flow *f, Bits dif) +{ + Flow *f1; + Reg *r1; + int z; + + for(f1 = f; f1 != nil; f1 = f1->s1) { + r1 = (Reg*)f1->data; + for(z=0; z<BITS; z++) { + dif.b[z] = (dif.b[z] & + ~(~r1->refbehind.b[z] & r1->refahead.b[z])) | + r1->set.b[z] | r1->regdiff.b[z]; + if(dif.b[z] != r1->regdiff.b[z]) { + r1->regdiff.b[z] = dif.b[z]; + change++; + } + } + if(f1->active) + break; + f1->active = 1; + for(z=0; z<BITS; z++) + dif.b[z] &= ~(~r1->calbehind.b[z] & r1->calahead.b[z]); + if(f1->s2 != nil) + synch(f1->s2, dif); + } +} + +static uint64 +allreg(uint64 b, Rgn *r) +{ + Var *v; + int i; + + v = var + r->varno; + r->regno = 0; + switch(v->etype) { + + default: + fatal("unknown etype %d/%E", bitno(b), v->etype); + break; + + 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 TPTR32: + case TPTR64: + i = thearch.BtoR(~b); + if(i && r->cost > 0) { + r->regno = i; + return thearch.RtoB(i); + } + break; + + case TFLOAT32: + case TFLOAT64: + i = thearch.BtoF(~b); + if(i && r->cost > 0) { + r->regno = i; + return thearch.FtoB(i); + } + break; + } + return 0; +} + +static void +paint1(Flow *f, int bn) +{ + Flow *f1; + Reg *r, *r1; + int z; + uint64 bb; + + z = bn/64; + bb = 1LL<<(bn%64); + r = (Reg*)f->data; + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + f1 = f->p1; + if(f1 == nil) + break; + r1 = (Reg*)f1->data; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + f = f1; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { + change -= CLOAD * f->loop; + } + for(;;) { + r->act.b[z] |= bb; + + if(f->prog->as != ANOP) { // don't give credit for NOPs + if(r->use1.b[z] & bb) + change += CREF * f->loop; + if((r->use2.b[z]|r->set.b[z]) & bb) + change += CREF * f->loop; + } + + if(STORE(r) & r->regdiff.b[z] & bb) { + change -= CLOAD * f->loop; + } + + if(r->refbehind.b[z] & bb) + for(f1 = f->p2; f1 != nil; f1 = f1->p2link) + if(((Reg*)f1->data)->refahead.b[z] & bb) + paint1(f1, bn); + + if(!(r->refahead.b[z] & bb)) + break; + f1 = f->s2; + if(f1 != nil) + if(((Reg*)f1->data)->refbehind.b[z] & bb) + paint1(f1, bn); + f = f->s1; + if(f == nil) + break; + r = (Reg*)f->data; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +static uint64 +paint2(Flow *f, int bn, int depth) +{ + Flow *f1; + Reg *r, *r1; + int z; + uint64 bb, vreg; + + z = bn/64; + bb = 1LL << (bn%64); + vreg = regbits; + r = (Reg*)f->data; + if(!(r->act.b[z] & bb)) + return vreg; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + f1 = f->p1; + if(f1 == nil) + break; + r1 = (Reg*)f1->data; + if(!(r1->refahead.b[z] & bb)) + break; + if(!(r1->act.b[z] & bb)) + break; + f = f1; + r = r1; + } + for(;;) { + if(debug['R'] && debug['v']) + print(" paint2 %d %P\n", depth, f->prog); + + r->act.b[z] &= ~bb; + + vreg |= r->regu; + + if(r->refbehind.b[z] & bb) + for(f1 = f->p2; f1 != nil; f1 = f1->p2link) + if(((Reg*)f1->data)->refahead.b[z] & bb) + vreg |= paint2(f1, bn, depth+1); + + if(!(r->refahead.b[z] & bb)) + break; + f1 = f->s2; + if(f1 != nil) + if(((Reg*)f1->data)->refbehind.b[z] & bb) + vreg |= paint2(f1, bn, depth+1); + f = f->s1; + if(f == nil) + break; + r = (Reg*)f->data; + if(!(r->act.b[z] & bb)) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } + + return vreg; +} + +static void +paint3(Flow *f, int bn, uint64 rb, int rn) +{ + Flow *f1; + Reg *r, *r1; + Prog *p; + int z; + uint64 bb; + + z = bn/64; + bb = 1LL << (bn%64); + r = (Reg*)f->data; + if(r->act.b[z] & bb) + return; + for(;;) { + if(!(r->refbehind.b[z] & bb)) + break; + f1 = f->p1; + if(f1 == nil) + break; + r1 = (Reg*)f1->data; + if(!(r1->refahead.b[z] & bb)) + break; + if(r1->act.b[z] & bb) + break; + f = f1; + r = r1; + } + + if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) + addmove(f, bn, rn, 0); + for(;;) { + r->act.b[z] |= bb; + p = f->prog; + + if(r->use1.b[z] & bb) { + if(debug['R'] && debug['v']) + print("%P", p); + addreg(&p->from, rn); + if(debug['R'] && debug['v']) + print(" ===change== %P\n", p); + } + if((r->use2.b[z]|r->set.b[z]) & bb) { + if(debug['R'] && debug['v']) + print("%P", p); + addreg(&p->to, rn); + if(debug['R'] && debug['v']) + print(" ===change== %P\n", p); + } + + if(STORE(r) & r->regdiff.b[z] & bb) + addmove(f, bn, rn, 1); + r->regu |= rb; + + if(r->refbehind.b[z] & bb) + for(f1 = f->p2; f1 != nil; f1 = f1->p2link) + if(((Reg*)f1->data)->refahead.b[z] & bb) + paint3(f1, bn, rb, rn); + + if(!(r->refahead.b[z] & bb)) + break; + f1 = f->s2; + if(f1 != nil) + if(((Reg*)f1->data)->refbehind.b[z] & bb) + paint3(f1, bn, rb, rn); + f = f->s1; + if(f == nil) + break; + r = (Reg*)f->data; + if(r->act.b[z] & bb) + break; + if(!(r->refbehind.b[z] & bb)) + break; + } +} + +static void +addreg(Adr *a, int rn) +{ + a->sym = nil; + a->node = nil; + a->offset = 0; + a->type = TYPE_REG; + a->reg = rn; + a->name = 0; + + ostats.ncvtreg++; +} + +void +dumpone(Flow *f, int isreg) +{ + int z; + Bits bit; + Reg *r; + + print("%d:%P", f->loop, f->prog); + if(isreg) { + r = (Reg*)f->data; + for(z=0; z<BITS; z++) + bit.b[z] = + r->set.b[z] | + r->use1.b[z] | + r->use2.b[z] | + r->refbehind.b[z] | + r->refahead.b[z] | + r->calbehind.b[z] | + r->calahead.b[z] | + r->regdiff.b[z] | + r->act.b[z] | + 0; + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print(" cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } + } + print("\n"); +} + +void +dumpit(char *str, Flow *r0, int isreg) +{ + Flow *r, *r1; + + print("\n%s\n", str); + for(r = r0; r != nil; r = r->link) { + dumpone(r, isreg); + r1 = r->p2; + if(r1 != nil) { + print(" pred:"); + for(; r1 != nil; r1 = r1->p2link) + print(" %.4ud", (int)r1->prog->pc); + if(r->p1 != nil) + print(" (and %.4ud)", (int)r->p1->prog->pc); + else + print(" (only)"); + print("\n"); + } + // Print successors if it's not just the next one + if(r->s1 != r->link || r->s2 != nil) { + print(" succ:"); + if(r->s1 != nil) + print(" %.4ud", (int)r->s1->prog->pc); + if(r->s2 != nil) + print(" %.4ud", (int)r->s2->prog->pc); + print("\n"); + } + } +} diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 80550f856d..0a4c1b8cbb 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -50,10 +50,10 @@ func eqstring(string, string) bool func intstring(*[4]byte, int64) string func slicebytetostring(*[32]byte, []byte) string func slicebytetostringtmp([]byte) string -func slicerunetostring([]rune) string -func stringtoslicebyte(string) []byte +func slicerunetostring(*[32]byte, []rune) string +func stringtoslicebyte(*[32]byte, string) []byte func stringtoslicebytetmp(string) []byte -func stringtoslicerune(string) []rune +func stringtoslicerune(*[32]rune, string) []rune func stringiter(string, int) int func stringiter2(string, int) (retk int, retv rune) func slicecopy(to any, fr any, wid uintptr) int @@ -86,7 +86,7 @@ func ifacethash(i1 any) (ret uint32) func efacethash(i1 any) (ret uint32) // *byte is really *runtime.Type -func makemap(mapType *byte, hint int64) (hmap map[any]any) +func makemap(mapType *byte, hint int64, mapbuf *any, bucketbuf *any) (hmap map[any]any) func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any) func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any) func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 5d3b71164a..537d0ca928 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -133,18 +133,16 @@ walkselect(Node *sel) break; case OSELRECV: - ch = n->right->left; - Selrecv1: - if(n->left == N) - n = n->right; - else - n->op = OAS; - break; - case OSELRECV2: ch = n->right->left; - if(n->ntest == N) - goto Selrecv1; + if(n->op == OSELRECV || n->ntest == N) { + if(n->left == N) + n = n->right; + else + n->op = OAS; + break; + } + if(n->left == N) { typecheck(&nblank, Erv | Easgn); n->left = nblank; diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 3d303eda09..1015950e41 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -302,13 +302,13 @@ staticcopy(Node *l, Node *r, NodeList **out) case OLITERAL: if(iszero(r)) return 1; - arch.gdata(l, r, l->type->width); + gdata(l, r, l->type->width); return 1; case OADDR: switch(r->left->op) { case ONAME: - arch.gdata(l, r, l->type->width); + gdata(l, r, l->type->width); return 1; } break; @@ -322,7 +322,7 @@ staticcopy(Node *l, Node *r, NodeList **out) case OSTRUCTLIT: case OMAPLIT: // copy pointer - arch.gdata(l, nod(OADDR, r->nname, N), l->type->width); + gdata(l, nod(OADDR, r->nname, N), l->type->width); return 1; } break; @@ -333,11 +333,11 @@ staticcopy(Node *l, Node *r, NodeList **out) a = r->nname; n1 = *l; n1.xoffset = l->xoffset + Array_array; - arch.gdata(&n1, nod(OADDR, a, N), widthptr); + gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - arch.gdata(&n1, r->right, widthint); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - arch.gdata(&n1, r->right, widthint); + gdata(&n1, r->right, widthint); return 1; } // fall through @@ -349,7 +349,7 @@ staticcopy(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + e->xoffset; n1.type = e->expr->type; if(e->expr->op == OLITERAL) - arch.gdata(&n1, e->expr, n1.type->width); + gdata(&n1, e->expr, n1.type->width); else { ll = nod(OXXX, N, N); *ll = n1; @@ -394,14 +394,14 @@ staticassign(Node *l, Node *r, NodeList **out) case OLITERAL: if(iszero(r)) return 1; - arch.gdata(l, r, l->type->width); + gdata(l, r, l->type->width); return 1; case OADDR: if(stataddr(&nam, r->left)) { n1 = *r; n1.left = &nam; - arch.gdata(l, &n1, l->type->width); + gdata(l, &n1, l->type->width); return 1; } @@ -417,7 +417,7 @@ staticassign(Node *l, Node *r, NodeList **out) // Init pointer. a = staticname(r->left->type, 1); r->nname = a; - arch.gdata(l, nod(OADDR, a, N), l->type->width); + gdata(l, nod(OADDR, a, N), l->type->width); // Init underlying literal. if(!staticassign(a, r->left, out)) *out = list(*out, nod(OAS, a, r->left)); @@ -444,11 +444,11 @@ staticassign(Node *l, Node *r, NodeList **out) r->nname = a; n1 = *l; n1.xoffset = l->xoffset + Array_array; - arch.gdata(&n1, nod(OADDR, a, N), widthptr); + gdata(&n1, nod(OADDR, a, N), widthptr); n1.xoffset = l->xoffset + Array_nel; - arch.gdata(&n1, r->right, widthint); + gdata(&n1, r->right, widthint); n1.xoffset = l->xoffset + Array_cap; - arch.gdata(&n1, r->right, widthint); + gdata(&n1, r->right, widthint); // Fall through to init underlying array. l = a; } @@ -462,7 +462,7 @@ staticassign(Node *l, Node *r, NodeList **out) n1.xoffset = l->xoffset + e->xoffset; n1.type = e->expr->type; if(e->expr->op == OLITERAL) - arch.gdata(&n1, e->expr, n1.type->width); + gdata(&n1, e->expr, n1.type->width); else { a = nod(OXXX, N, N); *a = n1; @@ -1223,7 +1223,7 @@ stataddr(Node *nam, Node *n) if(l < 0) break; // Check for overflow. - if(n->type->width != 0 && arch.MAXWIDTH/n->type->width <= l) + if(n->type->width != 0 && thearch.MAXWIDTH/n->type->width <= l) break; nam->xoffset += l*n->type->width; nam->type = n->type; @@ -1303,16 +1303,16 @@ gen_as_init(Node *n) case TPTR64: case TFLOAT32: case TFLOAT64: - arch.gdata(&nam, nr, nr->type->width); + gdata(&nam, nr, nr->type->width); break; case TCOMPLEX64: case TCOMPLEX128: - arch.gdatacomplex(&nam, nr->val.u.cval); + gdatacomplex(&nam, nr->val.u.cval); break; case TSTRING: - arch.gdatastring(&nam, nr->val.u.sval); + gdatastring(&nam, nr->val.u.sval); break; } @@ -1320,7 +1320,7 @@ yes: return 1; slice: - arch.gused(N); // in case the data is the dest of a goto + gused(N); // in case the data is the dest of a goto nl = nr; if(nr == N || nr->op != OADDR) goto no; @@ -1333,14 +1333,14 @@ slice: goto no; nam.xoffset += Array_array; - arch.gdata(&nam, nl, types[tptr]->width); + gdata(&nam, nl, types[tptr]->width); nam.xoffset += Array_nel-Array_array; nodconst(&nod1, types[TINT], nr->type->bound); - arch.gdata(&nam, &nod1, widthint); + gdata(&nam, &nod1, widthint); nam.xoffset += Array_cap-Array_nel; - arch.gdata(&nam, &nod1, widthint); + gdata(&nam, &nod1, widthint); goto yes; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 3ed194ee8f..f739a72499 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -38,6 +38,19 @@ parserline(void) return lineno; } +void +adderrorname(Node *n) +{ + char *old; + + if(n->op != ODOT) + return; + old = smprint("%L: undefined: %N\n", n->lineno, n->left); + if(nerr > 0 && err[nerr-1].lineno == n->lineno && strcmp(err[nerr-1].msg, old) == 0) + err[nerr-1].msg = smprint("%L: undefined: %N in %N\n", n->lineno, n->left, n); + free(old); +} + static void adderr(int line, char *fmt, va_list arg) { @@ -1211,7 +1224,7 @@ assignop(Type *src, Type *dst, char **why) *why = ""; // TODO(rsc,lvd): This behaves poorly in the presence of inlining. - // https://code.google.com/p/go/issues/detail?id=2795 + // https://golang.org/issue/2795 if(safemode && importpkg == nil && src != T && src->etype == TUNSAFEPTR) { yyerror("cannot use unsafe.Pointer"); errorexit(); @@ -1703,36 +1716,31 @@ ptrto(Type *t) void frame(int context) { - char *p; NodeList *l; Node *n; - int flag; + vlong w; - p = "stack"; - l = nil; - if(curfn) - l = curfn->dcl; if(context) { - p = "external"; + print("--- external frame ---\n"); l = externdcl; - } + } else if(curfn) { + print("--- %S frame ---\n", curfn->nname->sym); + l = curfn->dcl; + } else + return; - flag = 1; for(; l; l=l->next) { n = l->n; + w = -1; + if(n->type) + w = n->type->width; switch(n->op) { case ONAME: - if(flag) - print("--- %s frame ---\n", p); - print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type); - flag = 0; + print("%O %S G%d %T width=%lld\n", n->op, n->sym, n->vargen, n->type, w); break; case OTYPE: - if(flag) - print("--- %s frame ---\n", p); - print("%O %T\n", n->op, n->type); - flag = 0; + print("%O %T width=%lld\n", n->op, n->type, w); break; } } @@ -2121,10 +2129,10 @@ setmaxarg(Type *t, int32 extra) dowidth(t); w = t->argwid; - if(w >= arch.MAXWIDTH) + if(w >= thearch.MAXWIDTH) fatal("bad argwid %T", t); w += extra; - if(w >= arch.MAXWIDTH) + if(w >= thearch.MAXWIDTH) fatal("bad argwid %d + %T", extra, t); if(w > maxarg) maxarg = w; @@ -2628,7 +2636,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) inl_nonlocal = 0; curfn = nil; - funccompile(fn, 0); + funccompile(fn); } static Node* @@ -2876,7 +2884,7 @@ genhash(Sym *sym, Type *t) // an unexported field of type unsafe.Pointer. old_safemode = safemode; safemode = 0; - funccompile(fn, 0); + funccompile(fn); safemode = old_safemode; } @@ -3096,7 +3104,7 @@ geneq(Sym *sym, Type *t) // an unexported field of type unsafe.Pointer. old_safemode = safemode; safemode = 0; - funccompile(fn, 0); + funccompile(fn); safemode = old_safemode; } diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index ca5455d479..0dc0065ed9 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -454,31 +454,22 @@ 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: + if((arg != Strue && arg != Sfalse) || + assignop(n->left->type, exprname->type, nil) == OCONVIFACE || + assignop(exprname->type, n->left->type, nil) == OCONVIFACE) { a = nod(OIF, N, N); - a->ntest = n->left; // if val + a->ntest = nod(OEQ, exprname, n->left); // if name == val + typecheck(&a->ntest, Erv); a->nbody = list1(n->right); // then goto l - break; - - case Sfalse: + } else if(arg == Strue) { a = nod(OIF, N, N); - a->ntest = nod(ONOT, n->left, N); // if !val - typecheck(&a->ntest, Erv); + a->ntest = n->left; // if val a->nbody = list1(n->right); // then goto l - break; - - default: - snorm: + } else { // arg == Sfalse a = nod(OIF, N, N); - a->ntest = nod(OEQ, exprname, n->left); // if name == val + a->ntest = nod(ONOT, n->left, N); // if !val typecheck(&a->ntest, Erv); a->nbody = list1(n->right); // then goto l - break; } cas = list(cas, a); @@ -503,7 +494,7 @@ exprbsw(Case *c0, int ncase, int arg) /* * normal (expression) switch. - * rebulid case statements into if .. goto + * rebuild case statements into if .. goto */ static void exprswitch(Node *sw) @@ -533,12 +524,15 @@ exprswitch(Node *sw) */ exprname = N; cas = nil; - if(arg != Strue && arg != Sfalse) { + if(arg == Strue || arg == Sfalse) + exprname = nodbool(arg == Strue); + else if(consttype(sw->ntest) >= 0) + // leave constants to enable dead code elimination (issue 9608) + exprname = sw->ntest; + else { exprname = temp(sw->ntest->type); cas = list1(nod(OAS, exprname, sw->ntest)); typechecklist(cas, Etop); - } else { - exprname = nodbool(arg == Strue); } c0 = mkcaselist(sw, arg); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 635d2c4170..649f1c5120 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -555,168 +555,7 @@ reswitch: if(l->type == T || r->type == T) goto error; op = n->op; - arith: - if(op == OLSH || op == ORSH) - goto shift; - // ideal mixed with non-ideal - defaultlit2(&l, &r, 0); - n->left = l; - n->right = r; - if(l->type == T || r->type == T) - goto error; - t = l->type; - if(t->etype == TIDEAL) - t = r->type; - et = t->etype; - if(et == TIDEAL) - et = TINT; - if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) { - // comparison is okay as long as one side is - // assignable to the other. convert so they have - // the same type. - // - // the only conversion that isn't a no-op is concrete == interface. - // in that case, check comparability of the concrete type. - if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { - if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) { - yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type)); - goto error; - } - l = nod(aop, l, N); - l->type = r->type; - l->typecheck = 1; - n->left = l; - t = l->type; - goto converted; - } - if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) { - if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) { - yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type)); - goto error; - } - r = nod(aop, r, N); - r->type = l->type; - r->typecheck = 1; - n->right = r; - t = r->type; - } - converted: - et = t->etype; - } - if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { - defaultlit2(&l, &r, 1); - if(n->op == OASOP && n->implicit) { - yyerror("invalid operation: %N (non-numeric type %T)", n, l->type); - goto error; - } - yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type); - goto error; - } - if(!okfor[op][et]) { - yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t)); - goto error; - } - // okfor allows any array == array, map == map, func == func. - // restrict to slice/map/func == nil and nil == slice/map/func. - if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) { - yyerror("invalid operation: %N (%T cannot be compared)", n, l->type); - goto error; - } - if(isslice(l->type) && !isnil(l) && !isnil(r)) { - yyerror("invalid operation: %N (slice can only be compared to nil)", n); - goto error; - } - if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) { - yyerror("invalid operation: %N (map can only be compared to nil)", n); - goto error; - } - if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) { - yyerror("invalid operation: %N (func can only be compared to nil)", n); - goto error; - } - if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) { - yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype); - goto error; - } - - t = l->type; - if(iscmp[n->op]) { - evconst(n); - t = idealbool; - if(n->op != OLITERAL) { - defaultlit2(&l, &r, 1); - n->left = l; - n->right = r; - } - } else if(n->op == OANDAND || n->op == OOROR) { - if(l->type == r->type) - t = l->type; - else if(l->type == idealbool) - t = r->type; - else if(r->type == idealbool) - t = l->type; - // 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; - n->op = OCMPSTR; - } else if(n->op == OADD) { - // create OADDSTR node with list of strings in x + y + z + (w + v) + ... - n->op = OADDSTR; - if(l->op == OADDSTR) - n->list = l->list; - else - n->list = list1(l); - if(r->op == OADDSTR) - n->list = concat(n->list, r->list); - else - n->list = list(n->list, r); - n->left = N; - n->right = N; - } - } - if(et == TINTER) { - if(l->op == OLITERAL && l->val.ctype == CTNIL) { - // swap for back end - n->left = r; - n->right = l; - } else if(r->op == OLITERAL && r->val.ctype == CTNIL) { - // leave alone for back end - } else { - n->etype = n->op; - 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; - - shift: - defaultlit(&r, types[TUINT]); - n->right = r; - t = r->type; - if(!isint[t->etype] || issigned[t->etype]) { - yyerror("invalid operation: %N (shift count type %T, must be unsigned integer)", n, r->type); - goto error; - } - t = l->type; - if(t != T && t->etype != TIDEAL && !isint[t->etype]) { - yyerror("invalid operation: %N (shift of type %T)", n, t); - goto error; - } - // no defaultlit for left - // the outer context gives the type - n->type = l->type; - goto ret; + goto arith; case OCOM: case OMINUS: @@ -776,12 +615,14 @@ reswitch: case ODOT: typecheck(&n->left, Erv|Etype); defaultlit(&n->left, T); - if((t = n->left->type) == T) - goto error; if(n->right->op != ONAME) { yyerror("rhs of . must be a name"); // impossible goto error; } + if((t = n->left->type) == T) { + adderrorname(n); + goto error; + } r = n->right; if(n->left->op == OTYPE) { @@ -1442,41 +1283,7 @@ reswitch: goto ret; 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) { - 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 && n->type != types[TBOOL]) { - r = nod(OXXX, N, N); - n->op = OCONV; - n->orig = r; - *r = *n; - n->op = OLITERAL; - n->val = n->left->val; - } - break; - case OSTRARRAYBYTE: - // do not use stringtoarraylit. - // generated code and compiler memory footprint is better without it. - break; - case OSTRARRAYRUNE: - if(n->left->op == OLITERAL) - stringtoarraylit(&n); - break; - } - goto ret; + goto doconv; case OMAKE: ok |= Erv; @@ -1494,13 +1301,14 @@ reswitch: switch(t->etype) { default: - badmake: yyerror("cannot make type %T", t); goto error; case TARRAY: - if(!isslice(t)) - goto badmake; + if(!isslice(t)) { + yyerror("cannot make type %T", t); + goto error; + } if(args == nil) { yyerror("missing len argument to make(%T)", t); goto error; @@ -1793,6 +1601,216 @@ reswitch: checkwidth(n->left->type); goto ret; } + goto ret; + +arith: + if(op == OLSH || op == ORSH) + goto shift; + // ideal mixed with non-ideal + defaultlit2(&l, &r, 0); + n->left = l; + n->right = r; + if(l->type == T || r->type == T) + goto error; + t = l->type; + if(t->etype == TIDEAL) + t = r->type; + et = t->etype; + if(et == TIDEAL) + et = TINT; + aop = 0; + if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) { + // comparison is okay as long as one side is + // assignable to the other. convert so they have + // the same type. + // + // the only conversion that isn't a no-op is concrete == interface. + // in that case, check comparability of the concrete type. + // The conversion allocates, so only do it if the concrete type is huge. + if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { + if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type)); + goto error; + } + dowidth(l->type); + if(isinter(r->type) == isinter(l->type) || l->type->width >= 1<<16) { + l = nod(aop, l, N); + l->type = r->type; + l->typecheck = 1; + n->left = l; + } + t = r->type; + goto converted; + } + if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) { + if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type)); + goto error; + } + dowidth(r->type); + if(isinter(r->type) == isinter(l->type) || r->type->width >= 1<<16) { + r = nod(aop, r, N); + r->type = l->type; + r->typecheck = 1; + n->right = r; + } + t = l->type; + } + converted: + et = t->etype; + } + if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { + defaultlit2(&l, &r, 1); + if(n->op == OASOP && n->implicit) { + yyerror("invalid operation: %N (non-numeric type %T)", n, l->type); + goto error; + } + if(isinter(r->type) == isinter(l->type) || aop == 0) { + yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type); + goto error; + } + } + if(!okfor[op][et]) { + yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t)); + goto error; + } + // okfor allows any array == array, map == map, func == func. + // restrict to slice/map/func == nil and nil == slice/map/func. + if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) { + yyerror("invalid operation: %N (%T cannot be compared)", n, l->type); + goto error; + } + if(isslice(l->type) && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %N (slice can only be compared to nil)", n); + goto error; + } + if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %N (map can only be compared to nil)", n); + goto error; + } + if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %N (func can only be compared to nil)", n); + goto error; + } + if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) { + yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype); + goto error; + } + + t = l->type; + if(iscmp[n->op]) { + evconst(n); + t = idealbool; + if(n->op != OLITERAL) { + defaultlit2(&l, &r, 1); + n->left = l; + n->right = r; + } + } else if(n->op == OANDAND || n->op == OOROR) { + if(l->type == r->type) + t = l->type; + else if(l->type == idealbool) + t = r->type; + else if(r->type == idealbool) + t = l->type; + // 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; + n->op = OCMPSTR; + } else if(n->op == OADD) { + // create OADDSTR node with list of strings in x + y + z + (w + v) + ... + n->op = OADDSTR; + if(l->op == OADDSTR) + n->list = l->list; + else + n->list = list1(l); + if(r->op == OADDSTR) + n->list = concat(n->list, r->list); + else + n->list = list(n->list, r); + n->left = N; + n->right = N; + } + } + if(et == TINTER) { + if(l->op == OLITERAL && l->val.ctype == CTNIL) { + // swap for back end + n->left = r; + n->right = l; + } else if(r->op == OLITERAL && r->val.ctype == CTNIL) { + // leave alone for back end + } else if(isinter(r->type) == isinter(l->type)) { + n->etype = n->op; + 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; + +shift: + defaultlit(&r, types[TUINT]); + n->right = r; + t = r->type; + if(!isint[t->etype] || issigned[t->etype]) { + yyerror("invalid operation: %N (shift count type %T, must be unsigned integer)", n, r->type); + goto error; + } + t = l->type; + if(t != T && t->etype != TIDEAL && !isint[t->etype]) { + yyerror("invalid operation: %N (shift of type %T)", n, t); + goto error; + } + // no defaultlit for left + // the outer context gives the type + n->type = l->type; + goto ret; + +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) { + 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 && n->type != types[TBOOL]) { + r = nod(OXXX, N, N); + n->op = OCONV; + n->orig = r; + *r = *n; + n->op = OLITERAL; + n->val = n->left->val; + } + break; + case OSTRARRAYBYTE: + // do not use stringtoarraylit. + // generated code and compiler memory footprint is better without it. + break; + case OSTRARRAYRUNE: + if(n->left->op == OLITERAL) + stringtoarraylit(&n); + break; + } + goto ret; ret: t = n->type; @@ -1913,7 +1931,7 @@ checkdefergo(Node *n) case OPRINTN: case ORECOVER: // ok - break; + return; case OAPPEND: case OCAP: case OCOMPLEX: @@ -1927,23 +1945,21 @@ checkdefergo(Node *n) 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: - // type is broken or missing, most likely a method call on a broken type - // we will warn about the broken type elsewhere. no need to emit a potentially confusing error - if(n->left->type == T || n->left->type->broke) break; + yyerror("%s discards result of %N", what, n->left); + return; + } - 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; + // type is broken or missing, most likely a method call on a broken type + // we will warn about the broken type elsewhere. no need to emit a potentially confusing error + if(n->left->type == T || n->left->type->broke) + return; + + 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); } } @@ -3036,15 +3052,20 @@ typecheckas2(Node *n) goto out; switch(r->op) { case OINDEXMAP: - n->op = OAS2MAPR; - goto common; case ORECV: - n->op = OAS2RECV; - goto common; case ODOTTYPE: - n->op = OAS2DOTTYPE; - r->op = ODOTTYPE2; - common: + switch(r->op) { + case OINDEXMAP: + n->op = OAS2MAPR; + break; + case ORECV: + n->op = OAS2RECV; + break; + case ODOTTYPE: + n->op = OAS2DOTTYPE; + r->op = ODOTTYPE2; + break; + } if(l->type != T) checkassignto(r->type, l); if(l->defn == n) @@ -3293,6 +3314,8 @@ typecheckdef(Node *n) n->diag = 1; if(n->lineno != 0) lineno = n->lineno; + // Note: adderrorname looks for this string and + // adds context about the outer expression yyerror("undefined: %S", n->sym); } return n; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index efb283a1b8..0e69f88b66 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -628,6 +628,26 @@ walkexpr(Node **np, NodeList **init) goto ret; case OCALLFUNC: + if(n->left->op == OCLOSURE) { + // Transform direct call of a closure to call of a normal function. + // transformclosure already did all preparation work. + + // Append captured variables to argument list. + n->list = concat(n->list, n->left->enter); + n->left->enter = NULL; + // Replace OCLOSURE with ONAME/PFUNC. + n->left = n->left->closure->nname; + // Update type of OCALLFUNC node. + // Output arguments had not changed, but their offsets could. + if(n->left->type->outtuple == 1) { + t = getoutargx(n->left->type)->type; + if(t->etype == TFIELD) + t = t->type; + n->type = t; + } else + n->type = getoutargx(n->left->type); + } + t = n->left->type; if(n->list && n->list->n->op == OAS) goto ret; @@ -927,7 +947,7 @@ walkexpr(Node **np, NodeList **init) l->class = PEXTERN; l->xoffset = 0; sym->def = l; - arch.ggloblsym(sym, widthptr, DUPOK|NOPTR); + ggloblsym(sym, widthptr, DUPOK|NOPTR); } l = nod(OADDR, sym->def, N); l->addable = 1; @@ -995,7 +1015,7 @@ walkexpr(Node **np, NodeList **init) case OCONV: case OCONVNOP: - if(arch.thechar == '5') { + if(thearch.thechar == '5') { if(isfloat[n->left->type->etype]) { if(n->type->etype == TINT64) { n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); @@ -1273,6 +1293,7 @@ walkexpr(Node **np, NodeList **init) conv(n->right, types[TSTRING])); // quick check of len before full compare for == or != + // eqstring assumes that the lengths are equal 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); @@ -1329,12 +1350,32 @@ walkexpr(Node **np, NodeList **init) t = n->type; fn = syslook("makemap", 1); - argtype(fn, t->down); // any-1 - argtype(fn, t->type); // any-2 - n = mkcall1(fn, n->type, init, - typename(n->type), - conv(n->left, types[TINT64])); + a = nodnil(); // hmap buffer + r = nodnil(); // bucket buffer + if(n->esc == EscNone) { + // Allocate hmap buffer on stack. + var = temp(hmap(t)); + a = nod(OAS, var, N); // zero temp + typecheck(&a, Etop); + *init = list(*init, a); + a = nod(OADDR, var, N); + + // Allocate one bucket on stack. + // Maximum key/value size is 128 bytes, larger objects + // are stored with an indirection. So max bucket size is 2048+eps. + var = temp(mapbucket(t)); + r = nod(OAS, var, N); // zero temp + typecheck(&r, Etop); + *init = list(*init, r); + r = nod(OADDR, var, N); + } + + argtype(fn, hmap(t)); // hmap buffer + argtype(fn, mapbucket(t)); // bucket buffer + argtype(fn, t->down); // key type + argtype(fn, t->type); // value type + n = mkcall1(fn, n->type, init, typename(n->type), conv(n->left, types[TINT64]), a, r); goto ret; case OMAKESLICE: @@ -1397,13 +1438,25 @@ walkexpr(Node **np, NodeList **init) goto ret; case OARRAYRUNESTR: - // slicerunetostring([]rune) string; - n = mkcall("slicerunetostring", n->type, init, n->left); + // slicerunetostring(*[32]byte, []rune) string; + a = nodnil(); + if(n->esc == EscNone) { + // Create temporary buffer for string on stack. + t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); + a = nod(OADDR, temp(t), N); + } + n = mkcall("slicerunetostring", n->type, init, a, n->left); goto ret; case OSTRARRAYBYTE: - // stringtoslicebyte(string) []byte; - n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING])); + // stringtoslicebyte(*32[byte], string) []byte; + a = nodnil(); + if(n->esc == EscNone) { + // Create temporary buffer for slice on stack. + t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); + a = nod(OADDR, temp(t), N); + } + n = mkcall("stringtoslicebyte", n->type, init, a, conv(n->left, types[TSTRING])); goto ret; case OSTRARRAYBYTETMP: @@ -1412,8 +1465,14 @@ walkexpr(Node **np, NodeList **init) goto ret; case OSTRARRAYRUNE: - // stringtoslicerune(string) []rune - n = mkcall("stringtoslicerune", n->type, init, n->left); + // stringtoslicerune(*[32]rune, string) []rune + a = nodnil(); + if(n->esc == EscNone) { + // Create temporary buffer for slice on stack. + t = aindex(nodintconst(tmpstringbufsize), types[TINT32]); + a = nod(OADDR, temp(t), N); + } + n = mkcall("stringtoslicerune", n->type, init, a, n->left); goto ret; case OCMPIFACE: @@ -1601,7 +1660,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) l = tmp; } - a = nod(OAS, l, arch.nodarg(r, fp)); + a = nod(OAS, l, nodarg(r, fp)); a = convas(a, init); ullmancalc(a); if(a->ullman >= UINF) { @@ -1654,7 +1713,7 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Nod walkexpr(&n, init); } - a = nod(OAS, arch.nodarg(l, fp), n); + a = nod(OAS, nodarg(l, fp), n); nn = list(nn, convas(a, init)); return nn; } @@ -1734,7 +1793,7 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) { // optimization - can do block copy if(eqtypenoname(r->type, *nl)) { - a = arch.nodarg(*nl, fp); + a = nodarg(*nl, fp); r = nod(OCONVNOP, r, N); r->type = a->type; nn = list1(convas(nod(OAS, a, r), init)); @@ -1771,7 +1830,7 @@ loop: // argument to a ddd parameter then it is // passed thru unencapsulated if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { - a = nod(OAS, arch.nodarg(l, fp), r); + a = nod(OAS, nodarg(l, fp), r); a = convas(a, init); nn = list(nn, a); goto ret; @@ -1796,7 +1855,7 @@ loop: goto ret; } - a = nod(OAS, arch.nodarg(l, fp), r); + a = nod(OAS, nodarg(l, fp), r); a = convas(a, init); nn = list(nn, a); @@ -1944,8 +2003,7 @@ isstack(Node *n) { Node *defn; - while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type)) - n = n->left; + n = outervalue(n); // If n is *autotmp and autotmp = &foo, replace n with foo. // We introduce such temps when initializing struct literals. @@ -1976,8 +2034,7 @@ isstack(Node *n) static int isglobal(Node *n) { - while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type)) - n = n->left; + n = outervalue(n); switch(n->op) { case ONAME: @@ -2336,7 +2393,9 @@ Node* outervalue(Node *n) { for(;;) { - if(n->op == ODOT || n->op == OPAREN) { + if(n->op == OXDOT) + fatal("OXDOT in walk"); + if(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP) { n = n->left; continue; } @@ -2559,7 +2618,7 @@ paramstoheap(Type **argin, int out) // Defer might stop a panic and show the // return values as they exist at the time of panic. // Make sure to zero them on entry to the function. - nn = list(nn, nod(OAS, arch.nodarg(t, 1), N)); + nn = list(nn, nod(OAS, nodarg(t, 1), N)); } if(v == N || !(v->class & PHEAP)) continue; @@ -3300,10 +3359,51 @@ static void walkcompare(Node **np, NodeList **init) { Node *n, *l, *r, *call, *a, *li, *ri, *expr, *cmpl, *cmpr; + Node *x, *ok; int andor, i, needsize; Type *t, *t1; n = *np; + + // Given interface value l and concrete value r, rewrite + // l == r + // to + // x, ok := l.(type(r)); ok && x == r + // Handle != similarly. + // This avoids the allocation that would be required + // to convert r to l for comparison. + l = N; + r = N; + if(isinter(n->left->type) && !isinter(n->right->type)) { + l = n->left; + r = n->right; + } else if(!isinter(n->left->type) && isinter(n->right->type)) { + l = n->right; + r = n->left; + } + if(l != N) { + x = temp(r->type); + ok = temp(types[TBOOL]); + + // l.(type(r)) + a = nod(ODOTTYPE, l, N); + a->type = r->type; + + // x, ok := l.(type(r)) + expr = nod(OAS2, N, N); + expr->list = list1(x); + expr->list = list(expr->list, ok); + expr->rlist = list1(a); + typecheck(&expr, Etop); + walkexpr(&expr, init); + + if(n->op == OEQ) + r = nod(OANDAND, ok, nod(OEQ, x, r)); + else + r = nod(OOROR, nod(ONOT, ok, N), nod(ONE, x, r)); + *init = list(*init, expr); + goto ret; + } // Must be comparison of array or struct. // Otherwise back end handles it. @@ -3447,7 +3547,7 @@ walkrotate(Node **np) Node *l, *r; Node *n; - if(arch.thechar == '9') + if(thearch.thechar == '9') return; n = *np; @@ -3575,7 +3675,7 @@ walkdiv(Node **np, NodeList **init) Magic m; // TODO(minux) - if(arch.thechar == '9') + if(thearch.thechar == '9') return; n = *np; diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 398f83d113..7c92389767 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -14,32 +14,32 @@ Usage: The commands are: - build compile packages and dependencies - clean remove object files - env print Go environment information - fix run go tool fix on packages - fmt run gofmt on package sources - generate generate Go files by processing source - get download and install packages and dependencies - install compile and install packages and dependencies - list list packages - run compile and run Go program - test test packages - tool run specified go tool - version print Go version - vet run go tool vet on packages + build compile packages and dependencies + clean remove object files + env print Go environment information + fix run go tool fix on packages + fmt run gofmt on package sources + generate generate Go files by processing source + get download and install packages and dependencies + install compile and install packages and dependencies + list list packages + run compile and run Go program + test test packages + tool run specified go tool + version print Go version + vet run go tool vet on packages Use "go help [command]" for more information about a command. Additional help topics: - c calling between Go and C - filetype file types - gopath GOPATH environment variable - importpath import path syntax - packages description of package lists - testflag description of testing flags - testfunc description of testing functions + c calling between Go and C + filetype file types + gopath GOPATH environment variable + importpath import path syntax + packages description of package lists + testflag description of testing flags + testfunc description of testing functions Use "go help [topic]" for more information about that topic. @@ -310,6 +310,7 @@ The generator is run in the package's source directory. Go generate accepts one specific flag: -run="" + TODO: This flag is unimplemented. if non-empty, specifies a regular expression to select directives whose command matches the expression. diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go index 8c1e3ee230..3c0af8760b 100644 --- a/src/cmd/go/generate.go +++ b/src/cmd/go/generate.go @@ -106,6 +106,7 @@ The generator is run in the package's source directory. Go generate accepts one specific flag: -run="" + TODO: This flag is unimplemented. if non-empty, specifies a regular expression to select directives whose command matches the expression. diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 1482a39582..d5e86395a2 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -180,13 +180,13 @@ Usage: The commands are: {{range .}}{{if .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "go help [command]" for more information about a command. Additional help topics: {{range .}}{{if not .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} + {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} Use "go help [topic]" for more information about that topic. diff --git a/src/cmd/go/mkdoc.sh b/src/cmd/go/mkdoc.sh index e15e8809ce..507a8aeddc 100755 --- a/src/cmd/go/mkdoc.sh +++ b/src/cmd/go/mkdoc.sh @@ -1,9 +1,12 @@ -#!/bin/sh +#!/bin/bash # Copyright 2012 The Go Authors. All rights reserved. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -go install # So the next line will produce updated documentation. -go help documentation | sed 's; \*/; * /;' >doc.go +set -e + +go build -o go.latest +./go.latest help documentation | sed 's; \*/; * /;' >doc.go gofmt -w doc.go +rm go.latest diff --git a/src/cmd/go/test.go b/src/cmd/go/test.go index 60f6b16c9a..a8110f385d 100644 --- a/src/cmd/go/test.go +++ b/src/cmd/go/test.go @@ -346,11 +346,11 @@ func runTest(cmd *Command, args []string) { // been given on the command line (implicit current directory) // or when benchmarking. // Also stream if we're showing output anyway with a - // single package under test. In that case, streaming the - // output produces the same result as not streaming, - // just more immediately. + // single package under test or if parallelism is set to 1. + // In these cases, streaming the output produces the same result + // as not streaming, just more immediately. testStreamOutput = len(pkgArgs) == 0 || testBench || - (len(pkgs) <= 1 && testShowPass) + (testShowPass && (len(pkgs) == 1 || buildP == 1)) var b builder b.init() diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index 3fc0439548..9d0cd32862 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -87,6 +87,13 @@ When invoked with -s gofmt will make the following source transformations where for x, _ = range v {...} will be simplified to: for x = range v {...} + + A range of the form: + for _ = range v {...} + will be simplified to: + for range v {...} + +This may result in changes that are incompatible with earlier versions of Go. */ package main diff --git a/src/cmd/ld/Makefile b/src/cmd/ld/Makefile new file mode 100644 index 0000000000..3f528d7517 --- /dev/null +++ b/src/cmd/ld/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/ld/data.c b/src/cmd/ld/data.c index 0f287c202f..580d36f7e2 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -30,11 +30,14 @@ // Data layout and relocation. -#include "l.h" -#include "../ld/lib.h" -#include "../ld/elf.h" -#include "../ld/macho.h" -#include "../ld/pe.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "lib.h" +#include "elf.h" +#include "macho.h" +#include "pe.h" #include "../../runtime/mgc0.h" void dynreloc(void); @@ -150,7 +153,7 @@ relocsym(LSym *s) 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 & (SMASK | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) { + if(r->sym != nil && ((r->sym->type & (SMASK | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) { diag("%s: not defined", r->sym->name); continue; } @@ -160,9 +163,9 @@ relocsym(LSym *s) continue; // Solaris needs the ability to reference dynimport symbols. - if(HEADTYPE != Hsolaris && r->sym != S && r->sym->type == SDYNIMPORT) + if(HEADTYPE != Hsolaris && r->sym != nil && r->sym->type == SDYNIMPORT) diag("unhandled relocation for %s (type %d rtype %d)", r->sym->name, r->sym->type, r->type); - if(r->sym != S && r->sym->type != STLSBSS && !r->sym->reachable) + if(r->sym != nil && r->sym->type != STLSBSS && !r->sym->reachable) diag("unreachable sym in relocation: %s %s", s->name, r->sym->name); // Android emulates runtime.tlsg as a regular variable. @@ -172,11 +175,11 @@ relocsym(LSym *s) switch(r->type) { default: o = 0; - if(archreloc(r, s, &o) < 0) + if(thearch.archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case R_TLS: - if(linkmode == LinkInternal && iself && thechar == '5') { + if(linkmode == LinkInternal && iself && thearch.thechar == '5') { // On ELF ARM, the thread pointer is 8 bytes before // the start of the thread-local data block, so add 8 // to the actual TLS offset (r->sym->value). @@ -189,7 +192,7 @@ relocsym(LSym *s) } r->done = 0; o = 0; - if(thechar != '6') + if(thearch.thechar != '6') o = r->add; break; case R_TLS_LE: @@ -199,7 +202,7 @@ relocsym(LSym *s) r->xsym = ctxt->tlsg; r->xadd = r->add; o = 0; - if(thechar != '6') + if(thearch.thechar != '6') o = r->add; break; } @@ -213,7 +216,7 @@ relocsym(LSym *s) r->xsym = ctxt->tlsg; r->xadd = r->add; o = 0; - if(thechar != '6') + if(thearch.thechar != '6') o = r->add; break; } @@ -241,7 +244,7 @@ relocsym(LSym *s) o = r->xadd; if(iself) { - if(thechar == '6') + if(thearch.thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(rs->type != SHOSTOBJ) @@ -258,7 +261,7 @@ relocsym(LSym *s) // fail at runtime. See http://golang.org/issue/7980. // Instead of special casing only amd64, we treat this as an error on all // 64-bit architectures so as to be future-proof. - if((int32)o < 0 && PtrSize > 4 && siz == 4) { + if((int32)o < 0 && thearch.ptrsize > 4 && siz == 4) { diag("non-pc-relative relocation address is too big: %#llux", o); errorexit(); } @@ -283,7 +286,7 @@ relocsym(LSym *s) o = r->xadd; if(iself) { - if(thechar == '6') + if(thearch.thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { if(r->type == R_CALL) { @@ -314,7 +317,7 @@ relocsym(LSym *s) break; } if(r->variant != RV_NONE) - o = archrelocvariant(r, s, o); + o = thearch.archrelocvariant(r, s, o); //print("relocate %s %#llux (%#llux+%#llux, size %d) => %s %#llux +%#llx [%llx]\n", s->name, (uvlong)(s->value+off), (uvlong)s->value, (uvlong)r->off, r->siz, r->sym ? r->sym->name : "<nil>", (uvlong)symaddr(r->sym), (vlong)r->add, (vlong)o); switch(siz) { default: @@ -363,9 +366,9 @@ reloc(void) Bprint(&bso, "%5.2f reloc\n", cputime()); Bflush(&bso); - for(s=ctxt->textp; s!=S; s=s->next) + for(s=ctxt->textp; s!=nil; s=s->next) relocsym(s); - for(s=datap; s!=S; s=s->next) + for(s=datap; s!=nil; s=s->next) relocsym(s); } @@ -392,7 +395,7 @@ dynrelocsym(LSym *s) r->add = targ->plt; // jmp *addr - if(thechar == '8') { + if(thearch.thechar == '8') { adduint8(ctxt, rel, 0xff); adduint8(ctxt, rel, 0x25); addaddr(ctxt, rel, targ); @@ -414,10 +417,10 @@ dynrelocsym(LSym *s) } for(r=s->r; r<s->r+s->nr; r++) { - if(r->sym != S && r->sym->type == SDYNIMPORT || r->type >= 256) { - if(r->sym != S && !r->sym->reachable) + if(r->sym != nil && r->sym->type == SDYNIMPORT || r->type >= 256) { + if(r->sym != nil && !r->sym->reachable) diag("internal inconsistency: dynamic symbol %s is not reachable.", r->sym->name); - adddynrel(s, r); + thearch.adddynrel(s, r); } } } @@ -435,9 +438,9 @@ dynreloc(void) Bprint(&bso, "%5.2f reloc\n", cputime()); Bflush(&bso); - for(s=ctxt->textp; s!=S; s=s->next) + for(s=ctxt->textp; s!=nil; s=s->next) dynrelocsym(s); - for(s=datap; s!=S; s=s->next) + for(s=datap; s!=nil; s=s->next) dynrelocsym(s); if(iself) elfdynhash(); @@ -650,7 +653,7 @@ addstrdata(char *name, char *value) s->dupok = 1; reachable = s->reachable; addaddr(ctxt, s, sp); - adduintxx(ctxt, s, strlen(value), PtrSize); + adduintxx(ctxt, s, strlen(value), thearch.ptrsize); // addstring, addaddr, etc., mark the symbols as reachable. // In this case that is not necessarily true, so stick to what @@ -701,7 +704,7 @@ symalign(LSym *s) if(s->align != 0) return s->align; - align = MaxAlign; + align = thearch.maxalign; while(align > s->size && align > 1) align >>= 1; if(align < s->align) @@ -723,7 +726,7 @@ maxalign(LSym *s, int type) int32 align, max; max = 0; - for(; s != S && s->type <= type; s = s->next) { + for(; s != nil && s->type <= type; s = s->next) { align = symalign(s); if(max < align) max = align; @@ -789,7 +792,7 @@ proggenskip(ProgGen *g, vlong off, vlong v) vlong i; for(i = off; i < off+v; i++) { - if((i%PtrSize) == 0) + if((i%thearch.ptrsize) == 0) proggendata(g, BitsScalar); } } @@ -802,7 +805,7 @@ proggenarray(ProgGen *g, vlong len) proggendataflush(g); proggenemit(g, insArray); - for(i = 0; i < PtrSize; i++, len >>= 8) + for(i = 0; i < thearch.ptrsize; i++, len >>= 8) proggenemit(g, len); } @@ -843,39 +846,39 @@ proggenaddsym(ProgGen *g, LSym *s) // and not SDATA, but sometimes that doesn't happen. // Leave debugging the SDATA issue for the Go rewrite. - if(s->gotype == nil && s->size >= PtrSize && s->name[0] != '.') { + if(s->gotype == nil && s->size >= thearch.ptrsize && s->name[0] != '.') { // conservative scan diag("missing Go type information for global symbol: %s size %d", s->name, (int)s->size); - if((s->size%PtrSize) || (g->pos%PtrSize)) + if((s->size%thearch.ptrsize) || (g->pos%thearch.ptrsize)) diag("proggenaddsym: unaligned conservative symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); - size = (s->size+PtrSize-1)/PtrSize*PtrSize; - if(size < 32*PtrSize) { + size = (s->size+thearch.ptrsize-1)/thearch.ptrsize*thearch.ptrsize; + if(size < 32*thearch.ptrsize) { // Emit small symbols as data. - for(i = 0; i < size/PtrSize; i++) + for(i = 0; i < size/thearch.ptrsize; i++) proggendata(g, BitsPointer); } else { // Emit large symbols as array. - proggenarray(g, size/PtrSize); + proggenarray(g, size/thearch.ptrsize); proggendata(g, BitsPointer); proggenarrayend(g); } g->pos = s->value + size; - } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < PtrSize || s->name[0] == '.') { + } else if(s->gotype == nil || decodetype_noptr(s->gotype) || s->size < thearch.ptrsize || s->name[0] == '.') { // no scan - if(s->size < 32*PtrSize) { + if(s->size < 32*thearch.ptrsize) { // Emit small symbols as data. // This case also handles unaligned and tiny symbols, so tread carefully. for(i = s->value; i < s->value+s->size; i++) { - if((i%PtrSize) == 0) + if((i%thearch.ptrsize) == 0) proggendata(g, BitsScalar); } } else { // Emit large symbols as array. - if((s->size%PtrSize) || (g->pos%PtrSize)) + if((s->size%thearch.ptrsize) || (g->pos%thearch.ptrsize)) diag("proggenaddsym: unaligned noscan symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); - proggenarray(g, s->size/PtrSize); + proggenarray(g, s->size/thearch.ptrsize); proggendata(g, BitsScalar); proggenarrayend(g); } @@ -885,7 +888,7 @@ proggenaddsym(ProgGen *g, LSym *s) proggendataflush(g); gcprog = decodetype_gcprog(s->gotype); size = decodetype_size(s->gotype); - if((size%PtrSize) || (g->pos%PtrSize)) + if((size%thearch.ptrsize) || (g->pos%thearch.ptrsize)) diag("proggenaddsym: unaligned gcprog symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); for(i = 0; i < gcprog->np-1; i++) @@ -895,11 +898,11 @@ proggenaddsym(ProgGen *g, LSym *s) // gc mask, it's small so emit as data mask = decodetype_gcmask(s->gotype); size = decodetype_size(s->gotype); - if((size%PtrSize) || (g->pos%PtrSize)) + if((size%thearch.ptrsize) || (g->pos%thearch.ptrsize)) diag("proggenaddsym: unaligned gcmask symbol %s: size=%lld pos=%lld", s->name, s->size, g->pos); - for(i = 0; i < size; i += PtrSize) - proggendata(g, (mask[i/PtrSize/2]>>((i/PtrSize%2)*4+2))&BitsMask); + for(i = 0; i < size; i += thearch.ptrsize) + proggendata(g, (mask[i/thearch.ptrsize/2]>>((i/thearch.ptrsize%2)*4+2))&BitsMask); g->pos = s->value + size; } } @@ -935,7 +938,7 @@ dodata(void) last = nil; datap = nil; - for(s=ctxt->allsym; s!=S; s=s->allsym) { + for(s=ctxt->allsym; s!=nil; s=s->allsym) { if(!s->reachable || s->special) continue; if(STEXT < s->type && s->type < SXREF) { @@ -1134,7 +1137,7 @@ dodata(void) if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) { sect = addsection(&segdata, ".tbss", 06); - sect->align = PtrSize; + sect->align = thearch.ptrsize; sect->vaddr = 0; datsize = 0; for(; s != nil && s->type == STLSBSS; s = s->next) { @@ -1311,9 +1314,9 @@ textaddress(void) else va = rnd(va, funcalign); sym->value = 0; - for(sub = sym; sub != S; sub = sub->sub) + for(sub = sym; sub != nil; sub = sub->sub) sub->value += va; - if(sym->size == 0 && sym->sub != S) + if(sym->size == 0 && sym->sub != nil) ctxt->cursym = sym; if(sym->size < MINFUNC) va += MINFUNC; // spacing required for findfunctab diff --git a/src/cmd/ld/decodesym.c b/src/cmd/ld/decodesym.c index eedb246789..c194a1f767 100644 --- a/src/cmd/ld/decodesym.c +++ b/src/cmd/ld/decodesym.c @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" #include "../../runtime/typekind.h" @@ -70,42 +73,42 @@ decode_inuxi(uchar* p, int sz) static int commonsize(void) { - return 8*PtrSize + 8; + return 8*thearch.ptrsize + 8; } // Type.commonType.kind uint8 decodetype_kind(LSym *s) { - return s->p[1*PtrSize + 7] & KindMask; // 0x13 / 0x1f + return s->p[1*thearch.ptrsize + 7] & KindMask; // 0x13 / 0x1f } // Type.commonType.kind uint8 decodetype_noptr(LSym *s) { - return s->p[1*PtrSize + 7] & KindNoPointers; // 0x13 / 0x1f + return s->p[1*thearch.ptrsize + 7] & KindNoPointers; // 0x13 / 0x1f } // Type.commonType.kind uint8 decodetype_usegcprog(LSym *s) { - return s->p[1*PtrSize + 7] & KindGCProg; // 0x13 / 0x1f + return s->p[1*thearch.ptrsize + 7] & KindGCProg; // 0x13 / 0x1f } // Type.commonType.size vlong decodetype_size(LSym *s) { - return decode_inuxi(s->p, PtrSize); // 0x8 / 0x10 + return decode_inuxi(s->p, thearch.ptrsize); // 0x8 / 0x10 } // Type.commonType.gc LSym* decodetype_gcprog(LSym *s) { - return decode_reloc_sym(s, 1*PtrSize + 8 + 2*PtrSize); + return decode_reloc_sym(s, 1*thearch.ptrsize + 8 + 2*thearch.ptrsize); } uint8* @@ -113,7 +116,7 @@ decodetype_gcmask(LSym *s) { LSym *mask; - mask = decode_reloc_sym(s, 1*PtrSize + 8 + 1*PtrSize); + mask = decode_reloc_sym(s, 1*thearch.ptrsize + 8 + 1*thearch.ptrsize); return mask->p; } @@ -127,7 +130,7 @@ decodetype_arrayelem(LSym *s) vlong decodetype_arraylen(LSym *s) { - return decode_inuxi(s->p + commonsize()+2*PtrSize, PtrSize); + return decode_inuxi(s->p + commonsize()+2*thearch.ptrsize, thearch.ptrsize); } // Type.PtrType.elem @@ -147,7 +150,7 @@ decodetype_mapkey(LSym *s) LSym* decodetype_mapvalue(LSym *s) { - return decode_reloc_sym(s, commonsize()+PtrSize); // 0x20 / 0x38 + return decode_reloc_sym(s, commonsize()+thearch.ptrsize); // 0x20 / 0x38 } // Type.ChanType.elem @@ -168,13 +171,13 @@ decodetype_funcdotdotdot(LSym *s) int decodetype_funcincount(LSym *s) { - return decode_inuxi(s->p + commonsize()+2*PtrSize, IntSize); + return decode_inuxi(s->p + commonsize()+2*thearch.ptrsize, thearch.intsize); } int decodetype_funcoutcount(LSym *s) { - return decode_inuxi(s->p + commonsize()+3*PtrSize + 2*IntSize, IntSize); + return decode_inuxi(s->p + commonsize()+3*thearch.ptrsize + 2*thearch.intsize, thearch.intsize); } LSym* @@ -182,10 +185,10 @@ decodetype_funcintype(LSym *s, int i) { Reloc *r; - r = decode_reloc(s, commonsize() + PtrSize); + r = decode_reloc(s, commonsize() + thearch.ptrsize); if (r == nil) return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); + return decode_reloc_sym(r->sym, r->add + i * thearch.ptrsize); } LSym* @@ -193,23 +196,23 @@ decodetype_funcouttype(LSym *s, int i) { Reloc *r; - r = decode_reloc(s, commonsize() + 2*PtrSize + 2*IntSize); + r = decode_reloc(s, commonsize() + 2*thearch.ptrsize + 2*thearch.intsize); if (r == nil) return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); + return decode_reloc_sym(r->sym, r->add + i * thearch.ptrsize); } // Type.StructType.fields.Slice::len int decodetype_structfieldcount(LSym *s) { - return decode_inuxi(s->p + commonsize() + PtrSize, IntSize); + return decode_inuxi(s->p + commonsize() + thearch.ptrsize, thearch.intsize); } static int structfieldsize(void) { - return 5*PtrSize; + return 5*thearch.ptrsize; } // Type.StructType.fields[]-> name, typ and offset. @@ -219,7 +222,7 @@ decodetype_structfieldname(LSym *s, int i) Reloc *r; // go.string."foo" 0x28 / 0x40 - s = decode_reloc_sym(s, commonsize() + PtrSize + 2*IntSize + i*structfieldsize()); + s = decode_reloc_sym(s, commonsize() + thearch.ptrsize + 2*thearch.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 @@ -231,18 +234,18 @@ decodetype_structfieldname(LSym *s, int i) LSym* decodetype_structfieldtype(LSym *s, int i) { - return decode_reloc_sym(s, commonsize() + PtrSize + 2*IntSize + i*structfieldsize() + 2*PtrSize); + return decode_reloc_sym(s, commonsize() + thearch.ptrsize + 2*thearch.intsize + i*structfieldsize() + 2*thearch.ptrsize); } vlong decodetype_structfieldoffs(LSym *s, int i) { - return decode_inuxi(s->p + commonsize() + PtrSize + 2*IntSize + i*structfieldsize() + 4*PtrSize, IntSize); + return decode_inuxi(s->p + commonsize() + thearch.ptrsize + 2*thearch.intsize + i*structfieldsize() + 4*thearch.ptrsize, thearch.intsize); } // InterfaceTYpe.methods.len vlong decodetype_ifacemethodcount(LSym *s) { - return decode_inuxi(s->p + commonsize() + PtrSize, IntSize); + return decode_inuxi(s->p + commonsize() + thearch.ptrsize, thearch.intsize); } diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 061171ea0b..53f0194b31 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -12,13 +12,16 @@ // - file:line info for variables // - make strings a typedef so prettyprinters can see the underlying string type // -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" -#include "../ld/dwarf.h" -#include "../ld/dwarf_defs.h" -#include "../ld/elf.h" -#include "../ld/macho.h" -#include "../ld/pe.h" +#include "dwarf.h" +#include "dwarf_defs.h" +#include "elf.h" +#include "macho.h" +#include "pe.h" #include "../../runtime/typekind.h" /* @@ -75,12 +78,12 @@ static char gdbscript[1024]; static void addrput(vlong addr) { - switch(PtrSize) { + switch(thearch.ptrsize) { case 4: - LPUT(addr); + thearch.lput(addr); break; case 8: - VPUT(addr); + thearch.vput(addr); break; } } @@ -629,14 +632,14 @@ adddwarfrel(LSym* sec, LSym* sym, vlong offsetbase, int siz, vlong addend) r->type = R_ADDR; r->add = addend; r->xadd = addend; - if(iself && thechar == '6') + if(iself && thearch.thechar == '6') addend = 0; switch(siz) { case 4: - LPUT(addend); + thearch.lput(addend); break; case 8: - VPUT(addend); + thearch.vput(addend); break; default: diag("bad size in adddwarfrel"); @@ -663,7 +666,7 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) case DW_FORM_addr: // address if(linkmode == LinkExternal) { value -= ((LSym*)data)->value; - adddwarfrel(infosec, (LSym*)data, infoo, PtrSize, value); + adddwarfrel(infosec, (LSym*)data, infoo, thearch.ptrsize, value); break; } addrput(value); @@ -671,11 +674,11 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) case DW_FORM_block1: // block if(cls == DW_CLS_ADDRESS) { - cput(1+PtrSize); + cput(1+thearch.ptrsize); cput(DW_OP_addr); if(linkmode == LinkExternal) { value -= ((LSym*)data)->value; - adddwarfrel(infosec, (LSym*)data, infoo, PtrSize, value); + adddwarfrel(infosec, (LSym*)data, infoo, thearch.ptrsize, value); break; } addrput(value); @@ -689,14 +692,14 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) case DW_FORM_block2: // block value &= 0xffff; - WPUT(value); + thearch.wput(value); while(value--) cput(*data++); break; case DW_FORM_block4: // block value &= 0xffffffff; - LPUT(value); + thearch.lput(value); while(value--) cput(*data++); break; @@ -712,7 +715,7 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) break; case DW_FORM_data2: // constant - WPUT(value); + thearch.wput(value); break; case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr @@ -720,11 +723,11 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) adddwarfrel(infosec, linesym, infoo, 4, value); break; } - LPUT(value); + thearch.lput(value); break; case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr - VPUT(value); + thearch.vput(value); break; case DW_FORM_sdata: // constant @@ -750,16 +753,16 @@ putattr(int abbrev, int form, int cls, vlong value, char *data) // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. if (data == nil) { diag("dwarf: null reference in %d", abbrev); - if(PtrSize == 8) - VPUT(0); // invalid dwarf, gdb will complain. + if(thearch.ptrsize == 8) + thearch.vput(0); // invalid dwarf, gdb will complain. else - LPUT(0); // invalid dwarf, gdb will complain. + thearch.lput(0); // invalid dwarf, gdb will complain. } else { off = ((DWDie*)data)->offs; if (off == 0) fwdcount++; if(linkmode == LinkExternal) { - adddwarfrel(infosec, infosym, infoo, PtrSize, off); + adddwarfrel(infosec, infosym, infoo, thearch.ptrsize, off); break; } addrput(off); @@ -1236,17 +1239,17 @@ synthesizemaptypes(DWDie *die) // compute size info like hashmap.c does. a = getattr(keytype, DW_AT_byte_size); - keysize = a ? a->value : PtrSize; // We don't store size with Pointers + keysize = a ? a->value : thearch.ptrsize; // We don't store size with Pointers a = getattr(valtype, DW_AT_byte_size); - valsize = a ? a->value : PtrSize; + valsize = a ? a->value : thearch.ptrsize; indirect_key = 0; indirect_val = 0; if(keysize > MaxKeySize) { - keysize = PtrSize; + keysize = thearch.ptrsize; indirect_key = 1; } if(valsize > MaxValSize) { - valsize = PtrSize; + valsize = thearch.ptrsize; indirect_val = 1; } @@ -1288,12 +1291,12 @@ synthesizemaptypes(DWDie *die) fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow"); newrefattr(fld, DW_AT_type, defptrto(dwhb)); newmemberoffsetattr(fld, BucketSize + BucketSize * (keysize + valsize)); - if(RegSize > PtrSize) { + if(thearch.regsize > thearch.ptrsize) { fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad"); newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); - newmemberoffsetattr(fld, BucketSize + BucketSize * (keysize + valsize) + PtrSize); + newmemberoffsetattr(fld, BucketSize + BucketSize * (keysize + valsize) + thearch.ptrsize); } - newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + BucketSize * keysize + BucketSize * valsize + RegSize, 0); + newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + BucketSize * keysize + BucketSize * valsize + thearch.regsize, 0); // Construct hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1332,7 +1335,7 @@ synthesizechantypes(DWDie *die) continue; elemtype = (DWDie*) getattr(die, DW_AT_go_elem)->data; a = getattr(elemtype, DW_AT_byte_size); - elemsize = a ? a->value : PtrSize; + elemsize = a ? a->value : thearch.ptrsize; // sudog<T> dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1414,7 +1417,7 @@ movetomodule(DWDie *parent) die->link = parent->child; } -// If the pcln table contains runtime/string.goc, use that to set gdbscript path. +// If the pcln table contains runtime/runtime.go, use that to set gdbscript path. static void finddebugruntimepath(LSym *s) { @@ -1427,7 +1430,7 @@ finddebugruntimepath(LSym *s) for(i=0; i<s->pcln->nfile; i++) { f = s->pcln->file[i]; - if((p = strstr(f->name, "runtime/string.goc")) != nil) { + if((p = strstr(f->name, "runtime/runtime.go")) != nil) { *p = '\0'; snprint(gdbscript, sizeof gdbscript, "%sruntime/runtime-gdb.py", f->name); *p = 'r'; @@ -1519,9 +1522,9 @@ flushunit(DWDie *dwinfo, vlong pc, LSym *pcsym, vlong unitstart, int32 header_le here = cpos(); cseek(unitstart); - LPUT(here - unitstart - sizeof(int32)); // unit_length - WPUT(2); // dwarf version - LPUT(header_length); // header length starting here + thearch.lput(here - unitstart - sizeof(int32)); // unit_length + thearch.wput(2); // dwarf version + thearch.lput(header_length); // header length starting here cseek(here); } } @@ -1540,14 +1543,14 @@ writelines(void) Pciter pcfile, pcline; LSym **files, *f; - if(linesec == S) + if(linesec == nil) linesec = linklookup(ctxt, ".dwarfline", 0); linesec->nr = 0; unitstart = -1; headerend = -1; epc = 0; - epcs = S; + epcs = nil; lineo = cpos(); dwinfo = nil; @@ -1565,9 +1568,9 @@ writelines(void) // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf - LPUT(0); // unit_length (*), will be filled in by flushunit. - WPUT(2); // dwarf version (appendix F) - LPUT(0); // header_length (*), filled in by flushunit. + thearch.lput(0); // unit_length (*), will be filled in by flushunit. + thearch.wput(2); // dwarf version (appendix F) + thearch.lput(0); // header_length (*), filled in by flushunit. // cpos == unitstart + 4 + 2 + 4 cput(1); // minimum_instruction_length cput(1); // default_is_stmt @@ -1598,14 +1601,14 @@ writelines(void) headerend = cpos(); cput(0); // start extended opcode - uleb128put(1 + PtrSize); + uleb128put(1 + thearch.ptrsize); cput(DW_LNE_set_address); pc = s->value; line = 1; file = 1; if(linkmode == LinkExternal) - adddwarfrel(linesec, s, lineo, PtrSize, 0); + adddwarfrel(linesec, s, lineo, thearch.ptrsize, 0); else addrput(pc); @@ -1661,7 +1664,7 @@ writelines(void) switch (a->name) { case A_AUTO: dt = DW_ABRV_AUTO; - offs = a->aoffset - PtrSize; + offs = a->aoffset - thearch.ptrsize; break; case A_PARAM: dt = DW_ABRV_PARAM; @@ -1710,7 +1713,7 @@ writelines(void) enum { CIERESERVE = 16, - DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? + DATAALIGNMENTFACTOR = -4, // TODO -thearch.ptrsize? FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15? }; @@ -1727,10 +1730,10 @@ putpccfadelta(vlong deltapc, vlong cfa) cput(deltapc); } else if (deltapc < 0x10000) { cput(DW_CFA_advance_loc2); - WPUT(deltapc); + thearch.wput(deltapc); } else { cput(DW_CFA_advance_loc4); - LPUT(deltapc); + thearch.lput(deltapc); } } @@ -1742,14 +1745,14 @@ writeframes(void) Pciter pcsp; uint32 nextpc; - if(framesec == S) + if(framesec == nil) framesec = linklookup(ctxt, ".dwarfframe", 0); framesec->nr = 0; frameo = cpos(); // Emit the CIE, Section 6.4.1 - LPUT(CIERESERVE); // initial length, must be multiple of PtrSize - LPUT(0xffffffff); // cid. + thearch.lput(CIERESERVE); // initial length, must be multiple of thearch.ptrsize + thearch.lput(0xffffffff); // cid. cput(3); // dwarf version (appendix F) cput(0); // augmentation "" uleb128put(1); // code_alignment_factor @@ -1757,11 +1760,11 @@ writeframes(void) uleb128put(FAKERETURNCOLUMN); // return_address_register cput(DW_CFA_def_cfa); - uleb128put(DWARFREGSP); // register SP (**ABI-dependent, defined in l.h) - uleb128put(PtrSize); // offset + uleb128put(thearch.dwarfregsp); // register SP (**ABI-dependent, defined in l.h) + uleb128put(thearch.ptrsize); // offset cput(DW_CFA_offset + FAKERETURNCOLUMN); // return address - uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 + uleb128put(-thearch.ptrsize / DATAALIGNMENTFACTOR); // at cfa - x*4 // 4 is to exclude the length field. pad = CIERESERVE + frameo + 4 - cpos(); @@ -1778,8 +1781,8 @@ writeframes(void) fdeo = cpos(); // Emit a FDE, Section 6.4.1, starting wit a placeholder. - LPUT(0); // length, must be multiple of PtrSize - LPUT(0); // Pointer to the CIE above, at offset 0 + thearch.lput(0); // length, must be multiple of thearch.ptrsize + thearch.lput(0); // Pointer to the CIE above, at offset 0 addrput(0); // initial location addrput(0); // address range @@ -1792,23 +1795,23 @@ writeframes(void) if(nextpc < pcsp.pc) continue; } - putpccfadelta(nextpc - pcsp.pc, PtrSize + pcsp.value); + putpccfadelta(nextpc - pcsp.pc, thearch.ptrsize + pcsp.value); } fdesize = cpos() - fdeo - 4; // exclude the length field. - pad = rnd(fdesize, PtrSize) - fdesize; + pad = rnd(fdesize, thearch.ptrsize) - fdesize; strnput("", pad); fdesize += pad; // Emit the FDE header for real, Section 6.4.1. cseek(fdeo); - LPUT(fdesize); + thearch.lput(fdesize); if(linkmode == LinkExternal) { adddwarfrel(framesec, framesym, frameo, 4, 0); - adddwarfrel(framesec, s, frameo, PtrSize, 0); + adddwarfrel(framesec, s, frameo, thearch.ptrsize, 0); } else { - LPUT(0); + thearch.lput(0); addrput(s->value); } addrput(s->size); @@ -1834,11 +1837,11 @@ writeinfo(void) vlong unitstart, here; fwdcount = 0; - if (infosec == S) + if (infosec == nil) infosec = linklookup(ctxt, ".dwarfinfo", 0); infosec->nr = 0; - if(arangessec == S) + if(arangessec == nil) arangessec = linklookup(ctxt, ".dwarfaranges", 0); arangessec->nr = 0; @@ -1848,22 +1851,22 @@ writeinfo(void) // Write .debug_info Compilation Unit Header (sec 7.5.1) // Fields marked with (*) must be changed for 64-bit dwarf // This must match COMPUNITHEADERSIZE above. - LPUT(0); // unit_length (*), will be filled in later. - WPUT(2); // dwarf version (appendix F) + thearch.lput(0); // unit_length (*), will be filled in later. + thearch.wput(2); // dwarf version (appendix F) // debug_abbrev_offset (*) if(linkmode == LinkExternal) adddwarfrel(infosec, abbrevsym, infoo, 4, 0); else - LPUT(0); + thearch.lput(0); - cput(PtrSize); // address_size + cput(thearch.ptrsize); // address_size putdie(compunit); here = cpos(); cseek(unitstart); - LPUT(here - unitstart - 4); // exclude the length field. + thearch.lput(here - unitstart - 4); // exclude the length field. cseek(here); } cflush(); @@ -1910,22 +1913,22 @@ writepub(int (*ispub)(DWDie*)) unitend = infoo + infosize; // Write .debug_pubnames/types Header (sec 6.1.1) - LPUT(0); // unit_length (*), will be filled in later. - WPUT(2); // dwarf version (appendix F) - LPUT(unitstart); // debug_info_offset (of the Comp unit Header) - LPUT(unitend - unitstart); // debug_info_length + thearch.lput(0); // unit_length (*), will be filled in later. + thearch.wput(2); // dwarf version (appendix F) + thearch.lput(unitstart); // debug_info_offset (of the Comp unit Header) + thearch.lput(unitend - unitstart); // debug_info_length for (die = compunit->child; die != nil; die = die->link) { if (!ispub(die)) continue; - LPUT(die->offs - unitstart); + thearch.lput(die->offs - unitstart); dwa = getattr(die, DW_AT_name); strnput(dwa->data, dwa->value + 1); } - LPUT(0); + thearch.lput(0); here = cpos(); cseek(sectionstart); - LPUT(here - sectionstart - 4); // exclude the length field. + thearch.lput(here - sectionstart - 4); // exclude the length field. cseek(here); } @@ -1947,7 +1950,7 @@ writearanges(void) vlong value; sectionstart = cpos(); - headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself + headersize = rnd(4+2+4+1+1, thearch.ptrsize); // don't count unit_length field itself for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { b = getattr(compunit, DW_AT_low_pc); @@ -1958,21 +1961,21 @@ writearanges(void) continue; // Write .debug_aranges Header + entry (sec 6.1.2) - LPUT(headersize + 4*PtrSize - 4); // unit_length (*) - WPUT(2); // dwarf version (appendix F) + thearch.lput(headersize + 4*thearch.ptrsize - 4); // unit_length (*) + thearch.wput(2); // dwarf version (appendix F) value = compunit->offs - COMPUNITHEADERSIZE; // debug_info_offset if(linkmode == LinkExternal) adddwarfrel(arangessec, infosym, sectionstart, 4, value); else - LPUT(value); + thearch.lput(value); - cput(PtrSize); // address_size + cput(thearch.ptrsize); // address_size cput(0); // segment_size - strnput("", headersize - (4+2+4+1+1)); // align to PtrSize + strnput("", headersize - (4+2+4+1+1)); // align to thearch.ptrsize if(linkmode == LinkExternal) - adddwarfrel(arangessec, (LSym*)b->data, sectionstart, PtrSize, b->value-((LSym*)b->data)->value); + adddwarfrel(arangessec, (LSym*)b->data, sectionstart, thearch.ptrsize, b->value-((LSym*)b->data)->value); else addrput(b->value); @@ -2016,9 +2019,9 @@ writedwarfreloc(LSym* s) start = cpos(); for(r = s->r; r < s->r+s->nr; r++) { if(iself) - i = elfreloc1(r, r->off); + i = thearch.elfreloc1(r, r->off); else if(HEADTYPE == Hdarwin) - i = machoreloc1(r, r->off); + i = thearch.machoreloc1(r, r->off); else i = -1; if(i < 0) @@ -2062,7 +2065,7 @@ dwarfemitdebugsections(void) die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, thearch.ptrsize, 0); newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, KindUintptr, 0); // Needed by the prettyprinter code for interface inspection. @@ -2196,7 +2199,7 @@ dwarfaddshstrings(LSym *shstrtab) elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); if(linkmode == LinkExternal) { - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rela.debug_info"); elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rela.debug_aranges"); elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rela.debug_line"); @@ -2251,17 +2254,17 @@ dwarfaddelfrelocheader(int elfstr, ElfShdr *shdata, vlong off, vlong size) ElfShdr *sh; sh = newElfShdr(elfstrdbg[elfstr]); - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { sh->type = SHT_RELA; } else { sh->type = SHT_REL; } - sh->entsize = PtrSize*(2+(sh->type==SHT_RELA)); + sh->entsize = thearch.ptrsize*(2+(sh->type==SHT_RELA)); sh->link = elfshname(".symtab")->shnum; sh->info = shdata->shnum; sh->off = off; sh->size = size; - sh->addralign = PtrSize; + sh->addralign = thearch.ptrsize; } diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c index 89a0a5e87f..12ced98107 100644 --- a/src/cmd/ld/elf.c +++ b/src/cmd/ld/elf.c @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" -#include "../ld/elf.h" +#include "elf.h" /* * We use the 64-bit data structures on both 32- and 64-bit machines @@ -15,6 +18,8 @@ int iself; +int nelfsym = 1; + static int elf64; static ElfEhdr hdr; static ElfPhdr *phdr[NSECT]; @@ -42,7 +47,7 @@ elfinit(void) { iself = 1; - switch(thechar) { + switch(thearch.thechar) { // 64-bit architectures case '9': if(ctxt->arch->endian == BigEndian) @@ -77,14 +82,14 @@ elfinit(void) void elf64phdr(ElfPhdr *e) { - LPUT(e->type); - LPUT(e->flags); - VPUT(e->off); - VPUT(e->vaddr); - VPUT(e->paddr); - VPUT(e->filesz); - VPUT(e->memsz); - VPUT(e->align); + thearch.lput(e->type); + thearch.lput(e->flags); + thearch.vput(e->off); + thearch.vput(e->vaddr); + thearch.vput(e->paddr); + thearch.vput(e->filesz); + thearch.vput(e->memsz); + thearch.vput(e->align); } void @@ -103,44 +108,44 @@ elf32phdr(ElfPhdr *e) e->filesz += frag; e->memsz += frag; } - LPUT(e->type); - LPUT(e->off); - LPUT(e->vaddr); - LPUT(e->paddr); - LPUT(e->filesz); - LPUT(e->memsz); - LPUT(e->flags); - LPUT(e->align); + thearch.lput(e->type); + thearch.lput(e->off); + thearch.lput(e->vaddr); + thearch.lput(e->paddr); + thearch.lput(e->filesz); + thearch.lput(e->memsz); + thearch.lput(e->flags); + thearch.lput(e->align); } void elf64shdr(ElfShdr *e) { - LPUT(e->name); - LPUT(e->type); - VPUT(e->flags); - VPUT(e->addr); - VPUT(e->off); - VPUT(e->size); - LPUT(e->link); - LPUT(e->info); - VPUT(e->addralign); - VPUT(e->entsize); + thearch.lput(e->name); + thearch.lput(e->type); + thearch.vput(e->flags); + thearch.vput(e->addr); + thearch.vput(e->off); + thearch.vput(e->size); + thearch.lput(e->link); + thearch.lput(e->info); + thearch.vput(e->addralign); + thearch.vput(e->entsize); } void elf32shdr(ElfShdr *e) { - LPUT(e->name); - LPUT(e->type); - LPUT(e->flags); - LPUT(e->addr); - LPUT(e->off); - LPUT(e->size); - LPUT(e->link); - LPUT(e->info); - LPUT(e->addralign); - LPUT(e->entsize); + thearch.lput(e->name); + thearch.lput(e->type); + thearch.lput(e->flags); + thearch.lput(e->addr); + thearch.lput(e->off); + thearch.lput(e->size); + thearch.lput(e->link); + thearch.lput(e->info); + thearch.lput(e->addralign); + thearch.lput(e->entsize); } uint32 @@ -231,19 +236,19 @@ elf64writehdr(void) for (i = 0; i < EI_NIDENT; i++) cput(hdr.ident[i]); - WPUT(hdr.type); - WPUT(hdr.machine); - LPUT(hdr.version); - VPUT(hdr.entry); - VPUT(hdr.phoff); - VPUT(hdr.shoff); - LPUT(hdr.flags); - WPUT(hdr.ehsize); - WPUT(hdr.phentsize); - WPUT(hdr.phnum); - WPUT(hdr.shentsize); - WPUT(hdr.shnum); - WPUT(hdr.shstrndx); + thearch.wput(hdr.type); + thearch.wput(hdr.machine); + thearch.lput(hdr.version); + thearch.vput(hdr.entry); + thearch.vput(hdr.phoff); + thearch.vput(hdr.shoff); + thearch.lput(hdr.flags); + thearch.wput(hdr.ehsize); + thearch.wput(hdr.phentsize); + thearch.wput(hdr.phnum); + thearch.wput(hdr.shentsize); + thearch.wput(hdr.shnum); + thearch.wput(hdr.shstrndx); return ELF64HDRSIZE; } @@ -254,19 +259,19 @@ elf32writehdr(void) for (i = 0; i < EI_NIDENT; i++) cput(hdr.ident[i]); - WPUT(hdr.type); - WPUT(hdr.machine); - LPUT(hdr.version); - LPUT(hdr.entry); - LPUT(hdr.phoff); - LPUT(hdr.shoff); - LPUT(hdr.flags); - WPUT(hdr.ehsize); - WPUT(hdr.phentsize); - WPUT(hdr.phnum); - WPUT(hdr.shentsize); - WPUT(hdr.shnum); - WPUT(hdr.shstrndx); + thearch.wput(hdr.type); + thearch.wput(hdr.machine); + thearch.lput(hdr.version); + thearch.lput(hdr.entry); + thearch.lput(hdr.phoff); + thearch.lput(hdr.shoff); + thearch.lput(hdr.flags); + thearch.wput(hdr.ehsize); + thearch.wput(hdr.phentsize); + thearch.wput(hdr.phnum); + thearch.wput(hdr.shentsize); + thearch.wput(hdr.shnum); + thearch.wput(hdr.shstrndx); return ELF32HDRSIZE; } @@ -381,9 +386,9 @@ elfwritenotehdr(char *str, uint32 namesz, uint32 descsz, uint32 tag) // Write Elf_Note header. cseek(sh->off); - LPUT(namesz); - LPUT(descsz); - LPUT(tag); + thearch.lput(namesz); + thearch.lput(descsz); + thearch.lput(tag); return sh; } @@ -416,7 +421,7 @@ elfwritenetbsdsig(void) // Followed by NetBSD string and version. cwrite(ELF_NOTE_NETBSD_NAME, ELF_NOTE_NETBSD_NAMESZ + 1); - LPUT(ELF_NOTE_NETBSD_VERSION); + thearch.lput(ELF_NOTE_NETBSD_VERSION); return sh->size; } @@ -449,7 +454,7 @@ elfwriteopenbsdsig(void) // Followed by OpenBSD string and version. cwrite(ELF_NOTE_OPENBSD_NAME, ELF_NOTE_OPENBSD_NAMESZ); - LPUT(ELF_NOTE_OPENBSD_VERSION); + thearch.lput(ELF_NOTE_OPENBSD_VERSION); return sh->size; } @@ -525,7 +530,6 @@ elfwritebuildinfo(void) return sh->size; } -extern int nelfsym; int elfverneed; typedef struct Elfaux Elfaux; @@ -610,7 +614,7 @@ elfdynhash(void) memset(need, 0, nsym * sizeof need[0]); memset(chain, 0, nsym * sizeof chain[0]); memset(buckets, 0, nbucket * sizeof buckets[0]); - for(sy=ctxt->allsym; sy!=S; sy=sy->allsym) { + for(sy=ctxt->allsym; sy!=nil; sy=sy->allsym) { if (sy->dynid <= 0) continue; @@ -690,7 +694,7 @@ elfdynhash(void) elfwritedynentsym(s, DT_VERSYM, linklookup(ctxt, ".gnu.version", 0)); } - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { sy = linklookup(ctxt, ".rela.plt", 0); if(sy->size > 0) { elfwritedynent(s, DT_PLTREL, DT_RELA); @@ -816,7 +820,7 @@ elfshreloc(Section *sect) if(strcmp(sect->name, ".shstrtab") == 0 || strcmp(sect->name, ".tbss") == 0) return nil; - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { prefix = ".rela"; typ = SHT_RELA; } else { @@ -827,12 +831,12 @@ elfshreloc(Section *sect) snprint(buf, sizeof buf, "%s%s", prefix, sect->name); sh = elfshname(buf); sh->type = typ; - sh->entsize = RegSize*(2+(typ==SHT_RELA)); + sh->entsize = thearch.regsize*(2+(typ==SHT_RELA)); sh->link = elfshname(".symtab")->shnum; sh->info = sect->elfsect->shnum; sh->off = sect->reloff; sh->size = sect->rellen; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; return sh; } @@ -875,7 +879,7 @@ elfrelocsect(Section *sect, LSym *first) } if(r->xsym->elfsym == 0) diag("reloc %d to non-elf symbol %s (outer=%s) %d", r->type, r->sym->name, r->xsym->name, r->sym->type); - if(elfreloc1(r, sym->value+r->off - sect->vaddr) < 0) + if(thearch.elfreloc1(r, sym->value+r->off - sect->vaddr) < 0) diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); } } @@ -943,7 +947,7 @@ doelf(void) debug['s'] = 0; debug['d'] = 1; - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { addstring(shstrtab, ".rela.text"); addstring(shstrtab, ".rela.rodata"); addstring(shstrtab, ".rela.typelink"); @@ -966,7 +970,7 @@ doelf(void) if(flag_shared) { addstring(shstrtab, ".init_array"); - if(thechar == '6' || thechar == '9') + if(thearch.thechar == '6' || thearch.thechar == '9') addstring(shstrtab, ".rela.init_array"); else addstring(shstrtab, ".rel.init_array"); @@ -983,13 +987,13 @@ doelf(void) addstring(shstrtab, ".interp"); addstring(shstrtab, ".hash"); addstring(shstrtab, ".got"); - if(thechar == '9') + if(thearch.thechar == '9') addstring(shstrtab, ".glink"); addstring(shstrtab, ".got.plt"); addstring(shstrtab, ".dynamic"); addstring(shstrtab, ".dynsym"); addstring(shstrtab, ".dynstr"); - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { addstring(shstrtab, ".rela"); addstring(shstrtab, ".rela.plt"); } else { @@ -1004,7 +1008,7 @@ doelf(void) s = linklookup(ctxt, ".dynsym", 0); s->type = SELFROSECT; s->reachable = 1; - if(thechar == '6' || thechar == '9') + if(thearch.thechar == '6' || thearch.thechar == '9') s->size += ELF64SYMSIZE; else s->size += ELF32SYMSIZE; @@ -1018,7 +1022,7 @@ doelf(void) dynstr = s; /* relocation table */ - if(thechar == '6' || thechar == '9') + if(thearch.thechar == '6' || thearch.thechar == '9') s = linklookup(ctxt, ".rela", 0); else s = linklookup(ctxt, ".rel", 0); @@ -1031,7 +1035,7 @@ doelf(void) s->type = SELFGOT; // writable /* ppc64 glink resolver */ - if(thechar == '9') { + if(thearch.thechar == '9') { s = linklookup(ctxt, ".glink", 0); s->reachable = 1; s->type = SELFRXSECT; @@ -1048,16 +1052,16 @@ doelf(void) s = linklookup(ctxt, ".plt", 0); s->reachable = 1; - if(thechar == '9') + if(thearch.thechar == '9') // In the ppc64 ABI, .plt is a data section // written by the dynamic linker. s->type = SELFSECT; else s->type = SELFRXSECT; - elfsetupplt(); + thearch.elfsetupplt(); - if(thechar == '6' || thechar == '9') + if(thearch.thechar == '6' || thearch.thechar == '9') s = linklookup(ctxt, ".rela.plt", 0); else s = linklookup(ctxt, ".rel.plt", 0); @@ -1082,13 +1086,13 @@ doelf(void) */ elfwritedynentsym(s, DT_HASH, linklookup(ctxt, ".hash", 0)); elfwritedynentsym(s, DT_SYMTAB, linklookup(ctxt, ".dynsym", 0)); - if(thechar == '6' || thechar == '9') + if(thearch.thechar == '6' || thearch.thechar == '9') elfwritedynent(s, DT_SYMENT, ELF64SYMSIZE); else elfwritedynent(s, DT_SYMENT, ELF32SYMSIZE); elfwritedynentsym(s, DT_STRTAB, linklookup(ctxt, ".dynstr", 0)); elfwritedynentsymsize(s, DT_STRSZ, linklookup(ctxt, ".dynstr", 0)); - if(thechar == '6' || thechar == '9') { + if(thearch.thechar == '6' || thearch.thechar == '9') { elfwritedynentsym(s, DT_RELA, linklookup(ctxt, ".rela", 0)); elfwritedynentsymsize(s, DT_RELASZ, linklookup(ctxt, ".rela", 0)); elfwritedynent(s, DT_RELAENT, ELF64RELASIZE); @@ -1100,12 +1104,12 @@ doelf(void) if(rpath) elfwritedynent(s, DT_RUNPATH, addstring(dynstr, rpath)); - if(thechar == '9') + if(thearch.thechar == '9') elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".plt", 0)); else elfwritedynentsym(s, DT_PLTGOT, linklookup(ctxt, ".got.plt", 0)); - if(thechar == '9') + if(thearch.thechar == '9') elfwritedynent(s, DT_PPC64_OPT, 0); // Solaris dynamic linker can't handle an empty .rela.plt if @@ -1167,7 +1171,7 @@ asmbelf(vlong symo) Section *sect; eh = getElfEhdr(); - switch(thechar) { + switch(thearch.thechar) { default: diag("unknown architecture in asmbelf"); errorexit(); @@ -1228,22 +1232,22 @@ asmbelf(vlong symo) if(interpreter == nil) { switch(HEADTYPE) { case Hlinux: - interpreter = linuxdynld; + interpreter = thearch.linuxdynld; break; case Hfreebsd: - interpreter = freebsddynld; + interpreter = thearch.freebsddynld; break; case Hnetbsd: - interpreter = netbsddynld; + interpreter = thearch.netbsddynld; break; case Hopenbsd: - interpreter = openbsddynld; + interpreter = thearch.openbsddynld; break; case Hdragonfly: - interpreter = dragonflydynld; + interpreter = thearch.dragonflydynld; break; case Hsolaris: - interpreter = solarisdynld; + interpreter = thearch.solarisdynld; break; } } @@ -1304,7 +1308,7 @@ asmbelf(vlong symo) sh->entsize = ELF64SYMSIZE; else sh->entsize = ELF32SYMSIZE; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; sh->link = elfshname(".dynstr")->shnum; // sh->info = index of first non-local symbol (number of local symbols) shsym(sh, linklookup(ctxt, ".dynsym", 0)); @@ -1327,7 +1331,7 @@ asmbelf(vlong symo) sh = elfshname(".gnu.version_r"); sh->type = SHT_GNU_VERNEED; sh->flags = SHF_ALLOC; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; sh->info = elfverneed; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".gnu.version_r", 0)); @@ -1340,7 +1344,7 @@ asmbelf(vlong symo) sh->type = SHT_RELA; sh->flags = SHF_ALLOC; sh->entsize = ELF64RELASIZE; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; sh->link = elfshname(".dynsym")->shnum; sh->info = elfshname(".plt")->shnum; shsym(sh, linklookup(ctxt, ".rela.plt", 0)); @@ -1402,15 +1406,15 @@ asmbelf(vlong symo) sh = elfshname(".got"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = RegSize; - sh->addralign = RegSize; + sh->entsize = thearch.regsize; + sh->addralign = thearch.regsize; shsym(sh, linklookup(ctxt, ".got", 0)); sh = elfshname(".got.plt"); sh->type = SHT_PROGBITS; sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = RegSize; - sh->addralign = RegSize; + sh->entsize = thearch.regsize; + sh->addralign = thearch.regsize; shsym(sh, linklookup(ctxt, ".got.plt", 0)); } @@ -1418,7 +1422,7 @@ asmbelf(vlong symo) sh->type = SHT_HASH; sh->flags = SHF_ALLOC; sh->entsize = 4; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; sh->link = elfshname(".dynsym")->shnum; shsym(sh, linklookup(ctxt, ".hash", 0)); @@ -1426,8 +1430,8 @@ asmbelf(vlong symo) sh = elfshname(".dynamic"); sh->type = SHT_DYNAMIC; sh->flags = SHF_ALLOC+SHF_WRITE; - sh->entsize = 2*RegSize; - sh->addralign = RegSize; + sh->entsize = 2*thearch.regsize; + sh->addralign = thearch.regsize; sh->link = elfshname(".dynstr")->shnum; shsym(sh, linklookup(ctxt, ".dynamic", 0)); ph = newElfPhdr(); @@ -1446,7 +1450,7 @@ asmbelf(vlong symo) ph->type = PT_TLS; ph->flags = PF_R; ph->memsz = -ctxt->tlsoffset; - ph->align = RegSize; + ph->align = thearch.regsize; } } @@ -1454,12 +1458,12 @@ asmbelf(vlong symo) ph = newElfPhdr(); ph->type = PT_GNU_STACK; ph->flags = PF_W+PF_R; - ph->align = RegSize; + ph->align = thearch.regsize; ph = newElfPhdr(); ph->type = PT_PAX_FLAGS; ph->flags = 0x2a00; // mprotect, randexec, emutramp disabled - ph->align = RegSize; + ph->align = thearch.regsize; } elfobj: @@ -1501,7 +1505,7 @@ elfobj: if(linkmode == LinkInternal && !debug['d'] && HEADTYPE != Hopenbsd) { sh = elfshname(".tbss"); sh->type = SHT_NOBITS; - sh->addralign = RegSize; + sh->addralign = thearch.regsize; sh->size = -ctxt->tlsoffset; sh->flags = SHF_ALLOC | SHF_TLS | SHF_WRITE; } @@ -1511,8 +1515,8 @@ elfobj: sh->type = SHT_SYMTAB; sh->off = symo; sh->size = symsize; - sh->addralign = RegSize; - sh->entsize = 8+2*RegSize; + sh->addralign = thearch.regsize; + sh->entsize = 8+2*thearch.regsize; sh->link = elfshname(".strtab")->shnum; sh->info = elfglobalsymndx; diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 16c052fcb5..85589bd70f 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -1021,12 +1021,6 @@ void dwarfaddelfsectionsyms(void); void dwarfaddelfheaders(void); void asmbelf(vlong symo); void asmbelfsetup(void); -extern char linuxdynld[]; -extern char freebsddynld[]; -extern char netbsddynld[]; -extern char openbsddynld[]; -extern char dragonflydynld[]; -extern char solarisdynld[]; int elfreloc1(Reloc*, vlong sectoff); void putelfsectionsyms(void); diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index 9c296b740f..8b1123d3de 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -4,8 +4,11 @@ // go-specific code shared across loaders (5l, 6l, 8l). -#include "l.h" -#include "../ld/lib.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "lib.h" // accumulate all type information from .6 files. // check for inconsistencies. @@ -401,7 +404,7 @@ loadcgo(char *file, char *pkg, char *p, int n) // allow #pragma dynimport _ _ "foo.so" // to force a link of foo.so. havedynamic = 1; - adddynlib(lib); + thearch.adddynlib(lib); continue; } @@ -518,7 +521,7 @@ static LSym *emarkq; static void mark1(LSym *s, LSym *parent) { - if(s == S || s->reachable) + if(s == nil || s->reachable) return; if(strncmp(s->name, "go.weak.", 8) == 0) return; @@ -544,7 +547,7 @@ markflood(void) LSym *s; int i; - for(s=markq; s!=S; s=s->queue) { + for(s=markq; s!=nil; s=s->queue) { if(s->type == STEXT) { if(debug['v'] > 1) Bprint(&bso, "marktext %s\n", s->name); @@ -608,7 +611,7 @@ deadcode(void) markflood(); // keep each beginning with 'typelink.' if the symbol it points at is being kept. - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(strncmp(s->name, "go.typelink.", 12) == 0) s->reachable = s->nr==1 && s->r[0].sym->reachable; } @@ -630,7 +633,7 @@ deadcode(void) else last->next = nil; - for(s = ctxt->allsym; s != S; s = s->allsym) + for(s = ctxt->allsym; s != nil; s = s->allsym) if(strncmp(s->name, "go.weak.", 8) == 0) { s->special = 1; // do not lay out in data segment s->reachable = 1; @@ -639,7 +642,7 @@ deadcode(void) // record field tracking references fmtstrinit(&fmt); - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(strncmp(s->name, "go.track.", 9) == 0) { s->special = 1; // do not lay out in data segment s->hide = 1; @@ -668,7 +671,7 @@ doweak(void) // resolve weak references only if // target symbol will be in binary anyway. - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(strncmp(s->name, "go.weak.", 8) == 0) { t = linkrlookup(ctxt, s->name+8, s->version); if(t && t->type != 0 && t->reachable) { @@ -693,7 +696,7 @@ addexport(void) return; for(i=0; i<ndynexp; i++) - adddynsym(ctxt, dynexp[i]); + thearch.adddynsym(ctxt, dynexp[i]); } /* %Z from gc, for quoting import paths */ diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index 29d32d283d..7beff337f2 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -25,9 +25,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" -#include "../ld/elf.h" +#include "elf.h" enum { @@ -414,7 +417,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) return; } - switch(thechar) { + switch(thearch.thechar) { default: diag("%s: elf %s unimplemented", pn, thestring); return; @@ -590,7 +593,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) 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) + if(sym.sym == nil) continue; sect = obj->sect+sym.shndx; if(sect->sym == nil) { @@ -600,7 +603,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) continue; } s = sym.sym; - if(s->outer != S) { + if(s->outer != nil) { if(s->dupok) continue; diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); @@ -632,7 +635,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) // This keeps textp in increasing address order. for(i=0; i<obj->nsect; i++) { s = obj->sect[i].sym; - if(s == S) + if(s == nil) continue; if(s->sub) s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); @@ -645,7 +648,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) else ctxt->textp = s; ctxt->etextp = s; - for(s = s->sub; s != S; s = s->sub) { + for(s = s->sub; s != nil; s = s->sub) { if(s->onlist) sysfatal("symbol %s listed multiple times", s->name); s->onlist = 1; @@ -700,7 +703,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) continue; } if((info >> 32) == 0) { // absolute relocation, don't bother reading the null symbol - rp->sym = S; + rp->sym = nil; } else { if(readsym(obj, info>>32, &sym, 0) < 0) goto bad; @@ -844,7 +847,7 @@ readsym(ElfObj *obj, int i, ElfSym *sym, int needSym) } break; case ElfSymBindLocal: - if(thechar == '5' && (strncmp(sym->name, "$a", 2) == 0 || strncmp(sym->name, "$d", 2) == 0)) { + if(thearch.thechar == '5' && (strncmp(sym->name, "$a", 2) == 0 || strncmp(sym->name, "$d", 2) == 0)) { // binutils for arm generate these mapping // symbols, ignore these break; @@ -905,7 +908,7 @@ rbyoff(const void *va, const void *vb) static int reltype(char *pn, int elftype, uchar *siz) { - switch(R(thechar, elftype)) { + switch(R(thearch.thechar, elftype)) { default: diag("%s: unknown relocation type %d; compiled without -fpic?", pn, elftype); case R('9', R_PPC64_TOC16): diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index 71cfa63dec..4cbfa8d1ce 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -25,7 +25,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" enum { @@ -477,7 +480,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) m->len = len; m->name = pn; - switch(thechar) { + switch(thearch.thechar) { default: diag("%s: mach-o %s unimplemented", pn, thestring); return; @@ -627,7 +630,7 @@ 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->outer != nil) { if(s->dupok) continue; diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); @@ -652,13 +655,13 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) // 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) + if((s = sect->sym) == nil) continue; if(s->sub) { s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); // assign sizes, now that we know symbols in sorted order. - for(s1 = s->sub; s1 != S; s1 = s1->sub) { + for(s1 = s->sub; s1 != nil; s1 = s1->sub) { if(s1->sub) s1->size = s1->sub->value - s1->value; else @@ -674,7 +677,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) else ctxt->textp = s; ctxt->etextp = s; - for(s1 = s->sub; s1 != S; s1 = s1->sub) { + for(s1 = s->sub; s1 != nil; s1 = s1->sub) { if(s1->onlist) sysfatal("symbol %s listed multiple times", s1->name); s1->onlist = 1; @@ -687,7 +690,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) // load relocations for(i=0; i<c->seg.nsect; i++) { sect = &c->seg.sect[i]; - if((s = sect->sym) == S) + if((s = sect->sym) == nil) continue; macholoadrel(m, sect); if(sect->rel == nil) @@ -700,7 +703,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) int k; MachoSect *ks; - if(thechar != '8') { + if(thearch.thechar != '8') { // mach-o only uses scattered relocation on 32-bit platforms diag("unexpected scattered relocation"); continue; @@ -750,7 +753,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) werrstr("unsupported scattered relocation: invalid address %#ux", rel->addr); goto bad; foundk: - if(ks->sym != S) { + if(ks->sym != nil) { rp->sym = ks->sym; rp->add += rel->value - ks->addr; } else if(strcmp(ks->segname, "__IMPORT") == 0 && strcmp(ks->name, "__pointers") == 0) { @@ -788,7 +791,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) rp->off = rel->addr; // Handle X86_64_RELOC_SIGNED referencing a section (rel->extrn == 0). - if (thechar == '6' && rel->extrn == 0 && rel->type == 1) { + if (thearch.thechar == '6' && rel->extrn == 0 && rel->type == 1) { // Calculate the addend as the offset into the section. // // The rip-relative offset stored in the object file is encoded @@ -812,7 +815,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) // For i386 Mach-O PC-relative, the addend is written such that // it *is* the PC being subtracted. Use that to make // it match our version of PC-relative. - if(rel->pcrel && thechar == '8') + if(rel->pcrel && thearch.thechar == '8') rp->add += rp->off+rp->siz; if(!rel->extrn) { if(rel->symnum < 1 || rel->symnum > c->seg.nsect) { @@ -828,7 +831,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) // include that information in the addend. // We only care about the delta from the // section base. - if(thechar == '8') + if(thearch.thechar == '8') rp->add -= c->seg.sect[rel->symnum-1].addr; } else { if(rel->symnum >= symtab->nsym) { diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index 4f5e51f2f1..da11a3ea18 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -2,9 +2,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" -#include "../ld/pe.h" +#include "pe.h" #define IMAGE_SCN_MEM_DISCARDABLE 0x2000000 @@ -363,7 +366,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) if(sect == nil) return; - if(s->outer != S) { + if(s->outer != nil) { if(s->dupok) continue; diag("%s: duplicate symbol reference: %s in both %s and %s", pn, s->name, s->outer->name, sect->sym->name); @@ -386,7 +389,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) // This keeps textp in increasing address order. for(i=0; i<obj->nsect; i++) { s = obj->sect[i].sym; - if(s == S) + if(s == nil) continue; if(s->sub) s->sub = listsort(s->sub, valuecmp, offsetof(LSym, sub)); @@ -399,7 +402,7 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) else ctxt->textp = s; ctxt->etextp = s; - for(s = s->sub; s != S; s = s->sub) { + for(s = s->sub; s != nil; s = s->sub) { if(s->onlist) sysfatal("symbol %s listed multiple times", s->name); s->onlist = 1; @@ -458,7 +461,7 @@ readsym(PeObj *obj, int i, PeSym **y) name = sym->name; if(strncmp(name, "__imp_", 6) == 0) name = &name[6]; // __imp_Name => Name - if(thechar == '8' && name[0] == '_') + if(thearch.thechar == '8' && name[0] == '_') name = &name[1]; // _Name => Name } // remove last @XXX diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index fa08bc5f03..72c903b4d2 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -29,10 +29,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" -#include "../ld/elf.h" -#include "../ld/dwarf.h" +#include "elf.h" +#include "dwarf.h" #include "../../runtime/stack.h" #include "../../runtime/funcdata.h" @@ -105,7 +108,7 @@ libinit(void) { char *suffix, *suffixsep; - funcalign = FuncAlign; + funcalign = thearch.funcalign; fmtinstall('i', iconv); fmtinstall('Y', Yconv); fmtinstall('Z', Zconv); @@ -191,7 +194,7 @@ loadlib(void) } loadinternal("runtime"); - if(thechar == '5') + if(thearch.thechar == '5') loadinternal("math"); if(flag_race) loadinternal("runtime/race"); @@ -212,6 +215,14 @@ loadlib(void) // Force external linking for android. if(strcmp(goos, "android") == 0) linkmode = LinkExternal; + + // cgo on Darwin must use external linking + // we can always use external linking, but then there will be circular + // dependency problems when compiling natively (external linking requires + // runtime/cgo, runtime/cgo requires cmd/cgo, but cmd/cgo needs to be + // compiled using external linking.) + if(thearch.thechar == '5' && HEADTYPE == Hdarwin && iscgo) + linkmode = LinkExternal; } if(linkmode == LinkExternal && !iscgo) { @@ -243,7 +254,7 @@ loadlib(void) if(linkmode == LinkInternal) { // Drop all the cgo_import_static declarations. // Turns out we won't be needing them. - for(s = ctxt->allsym; s != S; s = s->allsym) + for(s = ctxt->allsym; s != nil; s = s->allsym) if(s->type == SHOSTOBJ) { // If a symbol was marked both // cgo_import_static and cgo_import_dynamic, @@ -257,8 +268,14 @@ loadlib(void) } tlsg = linklookup(ctxt, "runtime.tlsg", 0); - tlsg->type = STLSBSS; - tlsg->size = PtrSize; + // For most ports, runtime.tlsg is a placeholder symbol for TLS + // relocation. However, the Android and Darwin ports need it to + // be a real variable. Instead of hard-coding which platforms + // need it to be a real variable, we set the type to STLSBSS only + // when the runtime has not declared its type already. + if(tlsg->type == 0) + tlsg->type = STLSBSS; + tlsg->size = thearch.ptrsize; tlsg->hide = 1; tlsg->reachable = 1; ctxt->tlsg = tlsg; @@ -426,7 +443,7 @@ dowrite(int fd, char *p, int n) while(n > 0) { m = write(fd, p, n); if(m <= 0) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("write error: %r"); errorexit(); } @@ -515,7 +532,7 @@ hostobjs(void) h = &hostobj[i]; f = Bopen(h->file, OREAD); if(f == nil) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("cannot reopen %s: %r", h->pn); errorexit(); } @@ -589,7 +606,7 @@ hostlink(void) if(extld == nil) extld = "gcc"; argv[argc++] = extld; - switch(thechar){ + switch(thearch.thechar){ case '8': argv[argc++] = "-m32"; break; @@ -637,7 +654,7 @@ hostlink(void) h = &hostobj[i]; f = Bopen(h->file, OREAD); if(f == nil) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("cannot reopen %s: %r", h->pn); errorexit(); } @@ -646,7 +663,7 @@ hostlink(void) argv[argc++] = p; w = create(p, 1, 0775); if(w < 0) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("cannot create %s: %r", p); errorexit(); } @@ -658,7 +675,7 @@ hostlink(void) len -= n; } if(close(w) < 0) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("cannot write %s: %r", p); errorexit(); } @@ -705,7 +722,7 @@ hostlink(void) } if(runcmd(argv) < 0) { - ctxt->cursym = S; + ctxt->cursym = nil; diag("%s: running %s failed: %r", argv0, argv[0]); errorexit(); } @@ -760,7 +777,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, char *file, int whence) line[n] = '\0'; if(strncmp(line, "go object ", 10) != 0) { if(strlen(pn) > 3 && strcmp(pn+strlen(pn)-3, ".go") == 0) { - print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thechar, pn, thechar, thechar); + print("%cl: input %s is not .%c file (use %cg to compile .go files)\n", thearch.thechar, pn, thearch.thechar, thearch.thechar); errorexit(); } if(strcmp(line, thestring) == 0) { @@ -846,7 +863,7 @@ mywhatsys(void) goarch = getgoarch(); if(strncmp(goarch, thestring, strlen(thestring)) != 0) - sysfatal("cannot use %cc with GOARCH=%s", thechar, goarch); + sysfatal("cannot use %cc with GOARCH=%s", thearch.thechar, goarch); } int @@ -974,7 +991,7 @@ addsection(Segment *seg, char *name, int rwx) sect->rwx = rwx; sect->name = name; sect->seg = seg; - sect->align = PtrSize; // everything is at least pointer-aligned + sect->align = thearch.ptrsize; // everything is at least pointer-aligned *l = sect; return sect; } @@ -1032,20 +1049,21 @@ static void stkbroke(Chain*, int); static LSym *morestack; static LSym *newstack; -enum -{ - HasLinkRegister = (thechar == '5' || thechar == '9'), -}; - // TODO: Record enough information in new object files to // allow stack checks here. static int +haslinkregister(void) +{ + return thearch.thechar == '5' || thearch.thechar == '9'; +} + +static int callsize(void) { - if(HasLinkRegister) + if(haslinkregister()) return 0; - return RegSize; + return thearch.regsize; } void @@ -1166,8 +1184,8 @@ stkcheck(Chain *up, int depth) // to StackLimit beyond the frame size. if(strncmp(r->sym->name, "runtime.morestack", 17) == 0) { limit = StackLimit + s->locals; - if(HasLinkRegister) - limit += RegSize; + if(haslinkregister()) + limit += thearch.regsize; } break; @@ -1215,8 +1233,8 @@ stkprint(Chain *ch, int limit) else print("\t%d\tguaranteed after split check in %s\n", ch->limit, name); } else { - stkprint(ch->up, ch->limit + (!HasLinkRegister)*RegSize); - if(!HasLinkRegister) + stkprint(ch->up, ch->limit + callsize()); + if(!haslinkregister()) print("\t%d\ton entry to %s\n", ch->limit, name); } if(ch->limit != limit) @@ -1232,7 +1250,7 @@ Yconv(Fmt *fp) char *str; s = va_arg(fp->args, LSym*); - if (s == S) { + if (s == nil) { fmtprint(fp, "<nil>"); } else { fmtstrinit(&fmt); @@ -1317,7 +1335,7 @@ cwrite(void *buf, int n) void usage(void) { - fprint(2, "usage: %cl [options] main.%c\n", thechar, thechar); + fprint(2, "usage: %cl [options] main.%c\n", thearch.thechar, thearch.thechar); flagprint(2); exits("usage"); } @@ -1346,7 +1364,7 @@ setinterp(char *s) void doversion(void) { - print("%cl version %s\n", thechar, getgoversion()); + print("%cl version %s\n", thearch.thechar, getgoversion()); errorexit(); } @@ -1366,7 +1384,7 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) if(s->type == STEXT) put(s, s->name, 'T', s->value, s->size, s->version, 0); - for(s=ctxt->allsym; s!=S; s=s->allsym) { + for(s=ctxt->allsym; s!=nil; s=s->allsym) { if(s->hide || (s->name[0] == '.' && s->version == 0 && strcmp(s->name, ".rathole") != 0)) continue; switch(s->type&SMASK) { @@ -1406,7 +1424,7 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) put(s, s->name, 'T', s->value, s->size, s->version, s->gotype); // NOTE(ality): acid can't produce a stack trace without .frame symbols - put(nil, ".frame", 'm', s->locals+PtrSize, 0, 0, 0); + put(nil, ".frame", 'm', s->locals+thearch.ptrsize, 0, 0, 0); for(a=s->autom; a; a=a->link) { // Emit a or p according to actual offset, even if label is wrong. @@ -1418,7 +1436,7 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) if(a->name == A_PARAM) off = a->aoffset; else - off = a->aoffset - PtrSize; + off = a->aoffset - thearch.ptrsize; // FP if(off >= 0) { @@ -1427,8 +1445,8 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) } // SP - if(off <= -PtrSize) { - put(nil, a->asym->name, 'a', -(off+PtrSize), 0, 0, a->gotype); + if(off <= -thearch.ptrsize) { + put(nil, a->asym->name, 'a', -(off+thearch.ptrsize), 0, 0, a->gotype); continue; } @@ -1549,7 +1567,7 @@ diag(char *fmt, ...) tn = ""; sep = ""; - if(ctxt->cursym != S) { + if(ctxt->cursym != nil) { tn = ctxt->cursym->name; sep = ": "; } @@ -1617,3 +1635,18 @@ checkgo(void) } } } + +vlong +rnd(vlong v, vlong r) +{ + vlong c; + + if(r <= 0) + return v; + v += r - 1; + c = v % r; + if(c < 0) + c += r; + v -= c; + return v; +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index fd84c8bccb..f6a89535da 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -28,6 +28,58 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#ifndef EXTERN +#define EXTERN extern +#endif + +typedef struct Arch Arch; +struct Arch { + int thechar; + int ptrsize; + int intsize; + int regsize; + int funcalign; + int maxalign; + int minlc; + int dwarfregsp; + + char *linuxdynld; + char *freebsddynld; + char *netbsddynld; + char *openbsddynld; + char *dragonflydynld; + char *solarisdynld; + + void (*adddynlib)(char*); + void (*adddynrel)(LSym*, Reloc*); + void (*adddynsym)(Link*, LSym*); + void (*archinit)(void); + int (*archreloc)(Reloc*, LSym*, vlong*); + vlong (*archrelocvariant)(Reloc*, LSym*, vlong); + void (*asmb)(void); + int (*elfreloc1)(Reloc*, vlong); + void (*elfsetupplt)(void); + void (*gentext)(void); + void (*listinit)(void); + int (*machoreloc1)(Reloc*, vlong); + + void (*lput)(uint32); + void (*wput)(uint16); + void (*vput)(uint64); +}; + +vlong rnd(vlong, vlong); + +EXTERN Arch thearch; +EXTERN LSym* datap; +EXTERN int debug[128]; +EXTERN char literal[32]; +EXTERN int32 lcsize; +EXTERN char* rpath; +EXTERN int32 spsize; +EXTERN LSym* symlist; +EXTERN int32 symsize; + // Terrible but standard terminology. // A segment describes a block of file to load into memory. // A section further describes the pieces of that block for @@ -71,8 +123,8 @@ struct Section extern char symname[]; EXTERN char* INITENTRY; -extern char* thestring; -extern LinkArch* thelinkarch; +EXTERN char* thestring; +EXTERN LinkArch* thelinkarch; EXTERN char* outfile; EXTERN int ndynexp; EXTERN LSym** dynexp; @@ -82,6 +134,7 @@ EXTERN int havedynamic; EXTERN int funcalign; EXTERN int iscgo; EXTERN int elfglobalsymndx; +extern int nelfsym; EXTERN char* flag_installsuffix; EXTERN int flag_race; EXTERN int flag_shared; @@ -250,8 +303,8 @@ void libinit(void); LSym* listsort(LSym *l, int (*cmp)(LSym*, LSym*), int off); void loadinternal(char *name); void loadlib(void); -void lputb(int32 l); -void lputl(int32 l); +void lputb(uint32 l); +void lputl(uint32 l); void* mal(uint32 n); void mark(LSym *s); void mywhatsys(void); @@ -289,4 +342,8 @@ void zerosig(char *sp); void archinit(void); void diag(char *fmt, ...); +void ldmain(int, char**); + #pragma varargck argpos diag 1 + +#define SYMDEF "__.GOSYMDEF"
\ No newline at end of file diff --git a/src/cmd/ld/macho.c b/src/cmd/ld/macho.c index ffb20b3a55..b9b341031b 100644 --- a/src/cmd/ld/macho.c +++ b/src/cmd/ld/macho.c @@ -5,10 +5,13 @@ // Mach-O file writing // http://developer.apple.com/mac/library/DOCUMENTATION/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html -#include "l.h" -#include "../ld/dwarf.h" -#include "../ld/lib.h" -#include "../ld/macho.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "dwarf.h" +#include "lib.h" +#include "macho.h" static int macho64; static MachoHdr hdr; @@ -41,7 +44,7 @@ static void machodysymtab(void); void machoinit(void) { - switch(thechar) { + switch(thearch.thechar) { // 64-bit architectures case '6': case '9': @@ -146,85 +149,85 @@ machowrite(void) } if(macho64) - LPUT(0xfeedfacf); + thearch.lput(0xfeedfacf); else - LPUT(0xfeedface); - LPUT(hdr.cpu); - LPUT(hdr.subcpu); + thearch.lput(0xfeedface); + thearch.lput(hdr.cpu); + thearch.lput(hdr.subcpu); if(linkmode == LinkExternal) - LPUT(1); /* file type - mach object */ + thearch.lput(1); /* file type - mach object */ else - LPUT(2); /* file type - mach executable */ - LPUT(nload+nseg+ndebug); - LPUT(loadsize); - LPUT(1); /* flags - no undefines */ + thearch.lput(2); /* file type - mach executable */ + thearch.lput(nload+nseg+ndebug); + thearch.lput(loadsize); + thearch.lput(1); /* flags - no undefines */ if(macho64) - LPUT(0); /* reserved */ + thearch.lput(0); /* reserved */ for(i=0; i<nseg; i++) { s = &seg[i]; if(macho64) { - LPUT(25); /* segment 64 */ - LPUT(72+80*s->nsect); + thearch.lput(25); /* segment 64 */ + thearch.lput(72+80*s->nsect); strnput(s->name, 16); - VPUT(s->vaddr); - VPUT(s->vsize); - VPUT(s->fileoffset); - VPUT(s->filesize); - LPUT(s->prot1); - LPUT(s->prot2); - LPUT(s->nsect); - LPUT(s->flag); + thearch.vput(s->vaddr); + thearch.vput(s->vsize); + thearch.vput(s->fileoffset); + thearch.vput(s->filesize); + thearch.lput(s->prot1); + thearch.lput(s->prot2); + thearch.lput(s->nsect); + thearch.lput(s->flag); } else { - LPUT(1); /* segment 32 */ - LPUT(56+68*s->nsect); + thearch.lput(1); /* segment 32 */ + thearch.lput(56+68*s->nsect); strnput(s->name, 16); - LPUT(s->vaddr); - LPUT(s->vsize); - LPUT(s->fileoffset); - LPUT(s->filesize); - LPUT(s->prot1); - LPUT(s->prot2); - LPUT(s->nsect); - LPUT(s->flag); + thearch.lput(s->vaddr); + thearch.lput(s->vsize); + thearch.lput(s->fileoffset); + thearch.lput(s->filesize); + thearch.lput(s->prot1); + thearch.lput(s->prot2); + thearch.lput(s->nsect); + thearch.lput(s->flag); } for(j=0; j<s->nsect; j++) { t = &s->sect[j]; if(macho64) { strnput(t->name, 16); strnput(t->segname, 16); - VPUT(t->addr); - VPUT(t->size); - LPUT(t->off); - LPUT(t->align); - LPUT(t->reloc); - LPUT(t->nreloc); - LPUT(t->flag); - LPUT(t->res1); /* reserved */ - LPUT(t->res2); /* reserved */ - LPUT(0); /* reserved */ + thearch.vput(t->addr); + thearch.vput(t->size); + thearch.lput(t->off); + thearch.lput(t->align); + thearch.lput(t->reloc); + thearch.lput(t->nreloc); + thearch.lput(t->flag); + thearch.lput(t->res1); /* reserved */ + thearch.lput(t->res2); /* reserved */ + thearch.lput(0); /* reserved */ } else { strnput(t->name, 16); strnput(t->segname, 16); - LPUT(t->addr); - LPUT(t->size); - LPUT(t->off); - LPUT(t->align); - LPUT(t->reloc); - LPUT(t->nreloc); - LPUT(t->flag); - LPUT(t->res1); /* reserved */ - LPUT(t->res2); /* reserved */ + thearch.lput(t->addr); + thearch.lput(t->size); + thearch.lput(t->off); + thearch.lput(t->align); + thearch.lput(t->reloc); + thearch.lput(t->nreloc); + thearch.lput(t->flag); + thearch.lput(t->res1); /* reserved */ + thearch.lput(t->res2); /* reserved */ } } } for(i=0; i<nload; i++) { l = &load[i]; - LPUT(l->type); - LPUT(4*(l->ndata+2)); + thearch.lput(l->type); + thearch.lput(4*(l->ndata+2)); for(j=0; j<l->ndata; j++) - LPUT(l->data[j]); + thearch.lput(l->data[j]); } return cpos() - o1; @@ -353,10 +356,14 @@ asmbmacho(void) /* apple MACH */ va = INITTEXT - HEADR; mh = getMachoHdr(); - switch(thechar){ + switch(thearch.thechar){ default: diag("unknown mach architecture"); errorexit(); + case '5': + mh->cpu = MACHO_CPU_ARM; + mh->subcpu = MACHO_SUBCPU_ARMV7; + break; case '6': mh->cpu = MACHO_CPU_AMD64; mh->subcpu = MACHO_SUBCPU_X86; @@ -412,10 +419,16 @@ asmbmacho(void) machoshbits(ms, sect, "__DATA"); if(linkmode != LinkExternal) { - switch(thechar) { + switch(thearch.thechar) { default: diag("unknown macho architecture"); errorexit(); + case '5': + ml = newMachoLoad(5, 17+2); /* unix thread */ + ml->data[0] = 1; /* thread type */ + ml->data[1] = 17; /* word count */ + ml->data[2+15] = entryvalue(); /* start pc */ + break; case '6': ml = newMachoLoad(5, 42+2); /* unix thread */ ml->data[0] = 4; /* thread type */ @@ -605,7 +618,7 @@ machosymtab(void) adduint8(ctxt, symtab, 0x01); // type N_EXT, external symbol adduint8(ctxt, symtab, 0); // no section adduint16(ctxt, symtab, 0); // desc - adduintxx(ctxt, symtab, 0, PtrSize); // no value + adduintxx(ctxt, symtab, 0, thearch.ptrsize); // no value } else { if(s->cgoexport) adduint8(ctxt, symtab, 0x0f); @@ -620,7 +633,7 @@ machosymtab(void) } else adduint8(ctxt, symtab, o->sect->extnum); adduint16(ctxt, symtab, 0); // desc - adduintxx(ctxt, symtab, symaddr(s), PtrSize); + adduintxx(ctxt, symtab, symaddr(s), thearch.ptrsize); } } } @@ -746,7 +759,7 @@ machorelocsect(Section *sect, LSym *first) for(r = sym->r; r < sym->r+sym->nr; r++) { if(r->done) continue; - if(machoreloc1(r, sym->value+r->off - sect->vaddr) < 0) + if(thearch.machoreloc1(r, sym->value+r->off - sect->vaddr) < 0) diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name); } } diff --git a/src/cmd/ld/macho.h b/src/cmd/ld/macho.h index d759f4b0f8..d21109b38f 100644 --- a/src/cmd/ld/macho.h +++ b/src/cmd/ld/macho.h @@ -66,6 +66,9 @@ enum { MACHO_CPU_AMD64 = (1<<24)|7, MACHO_CPU_386 = 7, MACHO_SUBCPU_X86 = 3, + MACHO_CPU_ARM = 12, + MACHO_SUBCPU_ARM = 0, + MACHO_SUBCPU_ARMV7 = 9, MACHO32SYMSIZE = 12, MACHO64SYMSIZE = 16, @@ -80,6 +83,9 @@ enum { MACHO_X86_64_RELOC_SIGNED_2 = 7, MACHO_X86_64_RELOC_SIGNED_4 = 8, + MACHO_ARM_RELOC_VANILLA = 0, + MACHO_ARM_RELOC_BR24 = 5, + MACHO_GENERIC_RELOC_VANILLA = 0, MACHO_FAKE_GOTPCREL = 100, diff --git a/src/cmd/ld/pcln.c b/src/cmd/ld/pcln.c index f889b2c3ea..adb7661301 100644 --- a/src/cmd/ld/pcln.c +++ b/src/cmd/ld/pcln.c @@ -2,7 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include "l.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> #include "lib.h" #include "../../runtime/funcdata.h" @@ -104,6 +107,15 @@ renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d) *d = out; } +static int +container(LSym *s) +{ + // We want to generate func table entries only for the "lowest level" symbols, + // not containers of subsymbols. + if(s != nil && s->sub != nil) + return 1; + return 0; +} // pclntab initializes the pclntab symbol with // runtime function and file name information. @@ -111,7 +123,7 @@ void pclntab(void) { int32 i, nfunc, start, funcstart; - LSym *ftab, *s; + LSym *ftab, *s, *last; int32 off, end, frameptrsize; int64 funcdata_bytes; Pcln *pcln; @@ -125,35 +137,41 @@ pclntab(void) // See golang.org/s/go12symtab for the format. Briefly: // 8-byte header - // nfunc [PtrSize bytes] - // function table, alternating PC and offset to func struct [each entry PtrSize bytes] - // end PC [PtrSize bytes] + // nfunc [thearch.ptrsize bytes] + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] // offset to file table [4 bytes] nfunc = 0; - for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) - nfunc++; - symgrow(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4); + for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) { + if(!container(ctxt->cursym)) + nfunc++; + } + symgrow(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize+4); setuint32(ctxt, ftab, 0, 0xfffffffb); - setuint8(ctxt, ftab, 6, MINLC); - setuint8(ctxt, ftab, 7, PtrSize); - setuintxx(ctxt, ftab, 8, nfunc, PtrSize); + setuint8(ctxt, ftab, 6, thearch.minlc); + setuint8(ctxt, ftab, 7, thearch.ptrsize); + setuintxx(ctxt, ftab, 8, nfunc, thearch.ptrsize); nfunc = 0; - for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next, nfunc++) { + last = nil; + for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) { + last = ctxt->cursym; + if(container(ctxt->cursym)) + continue; pcln = ctxt->cursym->pcln; if(pcln == nil) pcln = &zpcln; funcstart = ftab->np; - funcstart += -ftab->np & (PtrSize-1); + funcstart += -ftab->np & (thearch.ptrsize-1); - setaddr(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize, ctxt->cursym); - setuintxx(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize); + setaddr(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize, ctxt->cursym); + setuintxx(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize, funcstart, thearch.ptrsize); // fixed size of struct, checked below off = funcstart; - end = funcstart + PtrSize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*PtrSize; - if(pcln->nfuncdata > 0 && (end&(PtrSize-1))) + end = funcstart + thearch.ptrsize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*thearch.ptrsize; + if(pcln->nfuncdata > 0 && (end&(thearch.ptrsize-1))) end += 4; symgrow(ctxt, ftab, end); @@ -173,7 +191,7 @@ pclntab(void) // when a called function doesn't have argument information. // We need to make sure everything has argument information // and then remove this. - frameptrsize = PtrSize; + frameptrsize = thearch.ptrsize; if(ctxt->cursym->leaf) frameptrsize = 0; off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize); @@ -203,38 +221,38 @@ pclntab(void) // funcdata, must be pointer-aligned and we're only int32-aligned. // Missing funcdata will be 0 (nil pointer). if(pcln->nfuncdata > 0) { - if(off&(PtrSize-1)) + if(off&(thearch.ptrsize-1)) off += 4; for(i=0; i<pcln->nfuncdata; i++) { if(pcln->funcdata[i] == nil) - setuintxx(ctxt, ftab, off+PtrSize*i, pcln->funcdataoff[i], PtrSize); + setuintxx(ctxt, ftab, off+thearch.ptrsize*i, pcln->funcdataoff[i], thearch.ptrsize); else { // TODO: Dedup. funcdata_bytes += pcln->funcdata[i]->size; - setaddrplus(ctxt, ftab, off+PtrSize*i, pcln->funcdata[i], pcln->funcdataoff[i]); + setaddrplus(ctxt, ftab, off+thearch.ptrsize*i, pcln->funcdata[i], pcln->funcdataoff[i]); } } - off += pcln->nfuncdata*PtrSize; + off += pcln->nfuncdata*thearch.ptrsize; } if(off != end) { - diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln->npcdata, pcln->nfuncdata, PtrSize); + diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln->npcdata, pcln->nfuncdata, thearch.ptrsize); errorexit(); } - // Final entry of table is just end pc. - if(ctxt->cursym->next == nil) - setaddrplus(ctxt, ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, ctxt->cursym, ctxt->cursym->size); + nfunc++; } + // Final entry of table is just end pc. + setaddrplus(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize, last, last->size); // Start file table. start = ftab->np; - start += -ftab->np & (PtrSize-1); - setuint32(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start); + start += -ftab->np & (thearch.ptrsize-1); + setuint32(ctxt, ftab, 8+thearch.ptrsize+nfunc*2*thearch.ptrsize+thearch.ptrsize, start); symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4); setuint32(ctxt, ftab, start, ctxt->nhistfile); - for(s = ctxt->filesyms; s != S; s = s->next) + for(s = ctxt->filesyms; s != nil; s = s->next) setuint32(ctxt, ftab, start + s->value*4, ftabaddstring(ftab, s->name)); ftab->size = ftab->np; @@ -246,6 +264,8 @@ pclntab(void) enum { BUCKETSIZE = 256*MINFUNC, SUBBUCKETS = 16, + SUBBUCKETSIZE = BUCKETSIZE/SUBBUCKETS, + NOIDX = 0x7fffffff }; // findfunctab generates a lookup table to quickly find the containing @@ -253,9 +273,10 @@ enum { void findfunctab(void) { - LSym *t, *s; - int32 idx, bidx, i, j, nbuckets; - vlong min, max; + LSym *t, *s, *e; + int32 idx, i, j, nbuckets, n, base; + vlong min, max, p, q; + int32 *indexes; t = linklookup(ctxt, "runtime.findfunctab", 0); t->type = SRODATA; @@ -267,33 +288,60 @@ findfunctab(void) for(s = ctxt->textp; s != nil; s = s->next) max = s->value + s->size; + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n = (max-min+SUBBUCKETSIZE-1)/SUBBUCKETSIZE; + indexes = (int32*)malloc(n*4); + if(indexes == nil) { + diag("out of memory"); + errorexit(); + } + for(i = 0; i < n; i++) + indexes[i] = NOIDX; + idx = 0; + for(s = ctxt->textp; s != nil; s = s->next) { + if(container(s)) + continue; + p = s->value; + e = s->next; + while(container(e)) + e = e->next; + if(e != nil) + q = e->value; + else + q = max; + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for(; p < q; p += SUBBUCKETSIZE) { + i = (p - min) / SUBBUCKETSIZE; + if(indexes[i] > idx) + indexes[i] = idx; + } + i = (q - 1 - min) / SUBBUCKETSIZE; + if(indexes[i] > idx) + indexes[i] = idx; + idx++; + } + // allocate table nbuckets = (max-min+BUCKETSIZE-1)/BUCKETSIZE; - symgrow(ctxt, t, nbuckets * (4+SUBBUCKETS)); + symgrow(ctxt, t, 4*nbuckets + n); // fill in table - s = ctxt->textp; - idx = 0; for(i = 0; i < nbuckets; i++) { - // Find first function which overlaps this bucket. - // Only do leaf symbols; skip symbols which are just containers (sub != nil but outer == nil). - while(s != nil && (s->value+s->size <= min + i * BUCKETSIZE || s->sub != nil && s->outer == nil)) { - s = s->next; - idx++; - } - // record this function in bucket header - setuint32(ctxt, t, i*(4+SUBBUCKETS), idx); - bidx = idx; - - // compute SUBBUCKETS deltas - for(j = 0; j < SUBBUCKETS; j++) { - while(s != nil && (s->value+s->size <= min + i * BUCKETSIZE + j * (BUCKETSIZE/SUBBUCKETS) || s->sub != nil && s->outer == nil)) { - s = s->next; - idx++; + base = indexes[i*SUBBUCKETS]; + if(base == NOIDX) + diag("hole in findfunctab"); + setuint32(ctxt, t, i*(4+SUBBUCKETS), base); + for(j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++) { + idx = indexes[i*SUBBUCKETS+j]; + if(idx == NOIDX) + diag("hole in findfunctab"); + if(idx - base >= 256) { + diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base); } - if(idx - bidx >= 256) - diag("too many functions in a findfunc bucket! %d %s", idx-bidx, s->name); - setuint8(ctxt, t, i*(4+SUBBUCKETS)+4+j, idx-bidx); + setuint8(ctxt, t, i*(4+SUBBUCKETS)+4+j, idx-base); } } + free(indexes); } diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index c26cd5264a..cb87778abd 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -5,10 +5,13 @@ // PE (Portable Executable) file writing // http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx -#include "l.h" -#include "../ld/lib.h" -#include "../ld/pe.h" -#include "../ld/dwarf.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "lib.h" +#include "pe.h" +#include "dwarf.h" // DOS stub that prints out // "This program cannot be run in DOS mode." @@ -139,7 +142,7 @@ peinit(void) { int32 l; - switch(thechar) { + switch(thearch.thechar) { // 64-bit architectures case '6': pe64 = 1; @@ -204,7 +207,7 @@ initdynimport(void) dr = nil; m = nil; - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(!s->reachable || s->type != SDYNIMPORT) continue; for(d = dr; d != nil; d = d->next) { @@ -234,9 +237,9 @@ initdynimport(void) m->s->sub = dynamic->sub; dynamic->sub = m->s; m->s->value = dynamic->size; - dynamic->size += PtrSize; + dynamic->size += thearch.ptrsize; } - dynamic->size += PtrSize; + dynamic->size += thearch.ptrsize; } return dr; @@ -344,7 +347,7 @@ initdynexport(void) LSym *s; nexport = 0; - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(!s->reachable || !(s->cgoexport & CgoExportDynamic)) continue; if(nexport+1 > sizeof(dexport)/sizeof(dexport[0])) { @@ -611,7 +614,7 @@ asmbpe(void) { IMAGE_SECTION_HEADER *t, *d; - switch(thechar) { + switch(thearch.thechar) { default: diag("unknown PE architecture"); errorexit(); @@ -646,7 +649,9 @@ asmbpe(void) addpersrc(); fh.NumberOfSections = nsect; - fh.TimeDateStamp = time(0); + // Being able to produce identical output for identical input is + // much more beneficial than having build timestamp in the header. + fh.TimeDateStamp = 0; fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED| IMAGE_FILE_EXECUTABLE_IMAGE|IMAGE_FILE_DEBUG_STRIPPED; if (pe64) { diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c index 8ecd18b817..0a48eb3a76 100644 --- a/src/cmd/ld/pobj.c +++ b/src/cmd/ld/pobj.c @@ -31,31 +31,33 @@ // Reading object files. #define EXTERN -#include "l.h" -#include "../ld/lib.h" -#include "../ld/elf.h" -#include "../ld/macho.h" -#include "../ld/dwarf.h" -#include "../ld/pe.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "lib.h" +#include "elf.h" +#include "macho.h" +#include "dwarf.h" +#include "pe.h" #include <ar.h> char *noname = "<none>"; char* paramspace = "FP"; void -main(int argc, char *argv[]) +ldmain(int argc, char **argv) { int i; - linkarchinit(); ctxt = linknew(thelinkarch); - ctxt->thechar = thechar; + ctxt->thechar = thearch.thechar; ctxt->thestring = thestring; ctxt->diag = diag; ctxt->bso = &bso; Binit(&bso, 1, OWRITE); - listinit(); + thearch.listinit(); memset(debug, 0, sizeof(debug)); nerrors = 0; outfile = nil; @@ -73,30 +75,30 @@ main(int argc, char *argv[]) if(strcmp(argv[i], "-crash_for_testing") == 0) *(volatile int*)0 = 0; - if(thechar == '5' && ctxt->goarm == 5) + if(thearch.thechar == '5' && ctxt->goarm == 5) debug['F'] = 1; flagcount("1", "use alternate profiling code", &debug['1']); - if(thechar == '6') + if(thearch.thechar == '6') flagcount("8", "assume 64-bit addresses", &debug['8']); flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); flagcount("C", "check Go calls to C code", &debug['C']); flagint64("D", "addr: data address", &INITDAT); flagstr("E", "sym: entry symbol", &INITENTRY); - if(thechar == '5') + if(thearch.thechar == '5') 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']); - if(thechar == '5') + if(thearch.thechar == '5') flagcount("M", "disable software div/mod", &debug['M']); flagcount("O", "print pc-line tables", &debug['O']); flagcount("Q", "debug byte-register code gen", &debug['Q']); - if(thechar == '5') + if(thearch.thechar == '5') flagcount("P", "debug code generation", &debug['P']); flagint32("R", "rnd: address rounding", &INITRND); - flagcount("S", "check type signatures", &debug['S']); + flagcount("nil", "check type signatures", &debug['S']); flagint64("T", "addr: text address", &INITTEXT); flagfn0("V", "print version and exit", doversion); flagcount("W", "disassemble input", &debug['W']); @@ -117,7 +119,7 @@ main(int argc, char *argv[]) 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']); - if(thechar == '5' || thechar == '6') + if(thearch.thechar == '5' || thearch.thechar == '6') flagcount("shared", "generate shared object (implies -linkmode external)", &flag_shared); flagstr("tmpdir", "dir: leave temporary files in this directory", &tmpdir); flagcount("u", "reject unsafe packages", &debug['u']); @@ -139,9 +141,9 @@ main(int argc, char *argv[]) if(outfile == nil) { if(HEADTYPE == Hwindows) - outfile = smprint("%c.out.exe", thechar); + outfile = smprint("%c.out.exe", thearch.thechar); else - outfile = smprint("%c.out", thechar); + outfile = smprint("%c.out", thearch.thechar); } libinit(); // creates outfile @@ -151,7 +153,7 @@ main(int argc, char *argv[]) if(headstring == nil) headstring = headstr(HEADTYPE); - archinit(); + thearch.archinit(); ctxt->debugfloat = debug['F']; if(debug['v']) @@ -165,7 +167,7 @@ main(int argc, char *argv[]) addlibpath(ctxt, "command line", "command line", argv[0], "main"); loadlib(); - if(thechar == '5') { + if(thearch.thechar == '5') { // mark some functions that are only referenced after linker code editing if(debug['F']) mark(linkrlookup(ctxt, "_sfloat", 0)); @@ -184,7 +186,7 @@ main(int argc, char *argv[]) if(HEADTYPE == Hwindows) dope(); addexport(); - gentext(); // trampolines, call stubs, etc. + thearch.gentext(); // trampolines, call stubs, etc. textaddress(); pclntab(); findfunctab(); @@ -193,7 +195,7 @@ main(int argc, char *argv[]) address(); doweak(); reloc(); - asmb(); + thearch.asmb(); undef(); hostlink(); if(debug['v']) { diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index c3a72c3cf6..55ef608c1d 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -30,9 +30,12 @@ // Symbol table. -#include "l.h" -#include "../ld/lib.h" -#include "../ld/elf.h" +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <link.h> +#include "lib.h" +#include "elf.h" static int maxelfstr; @@ -76,24 +79,24 @@ putelfstr(char *s) static void putelfsyment(int off, vlong addr, vlong size, int info, int shndx, int other) { - switch(thechar) { + switch(thearch.thechar) { case '6': case '9': - LPUT(off); + thearch.lput(off); cput(info); cput(other); - WPUT(shndx); - VPUT(addr); - VPUT(size); + thearch.wput(shndx); + thearch.vput(addr); + thearch.vput(size); symsize += ELF64SYMSIZE; break; default: - LPUT(off); - LPUT(addr); - LPUT(size); + thearch.lput(off); + thearch.lput(addr); + thearch.lput(size); cput(info); cput(other); - WPUT(shndx); + thearch.wput(shndx); symsize += ELF32SYMSIZE; break; } @@ -172,7 +175,7 @@ putelfsymshndx(vlong sympos, int shndx) vlong here; here = cpos(); - switch(thechar) { + switch(thearch.thechar) { case '6': cseek(sympos+6); break; @@ -180,7 +183,7 @@ putelfsymshndx(vlong sympos, int shndx) cseek(sympos+14); break; } - WPUT(shndx); + thearch.wput(shndx); cseek(here); } @@ -218,7 +221,7 @@ asmelfsym(void) elfglobalsymndx = numelfsym; genasmsym(putelfsym); - for(s=ctxt->allsym; s!=S; s=s->allsym) { + for(s=ctxt->allsym; s!=nil; s=s->allsym) { if(s->type != SHOSTOBJ && !(s->type == SDYNIMPORT && s->reachable)) continue; if(s->type == SDYNIMPORT) @@ -253,7 +256,7 @@ putplan9sym(LSym *x, char *s, int t, vlong addr, vlong size, int ver, LSym *go) case 'Z': case 'm': l = 4; - if(HEADTYPE == Hplan9 && thechar == '6' && !debug['8']) { + if(HEADTYPE == Hplan9 && thearch.thechar == '6' && !debug['8']) { lputb(addr>>32); l = 8; } @@ -307,7 +310,7 @@ wputb(ushort w) } void -lputb(int32 l) +lputb(uint32 l) { cput(l>>24); cput(l>>16); @@ -316,7 +319,7 @@ lputb(int32 l) } void -lputl(int32 l) +lputl(uint32 l) { cput(l); cput(l>>8); @@ -408,7 +411,7 @@ symtab(void) // within a type they sort by size, so the .* symbols // just defined above will be first. // hide the specific symbols. - for(s = ctxt->allsym; s != S; s = s->allsym) { + for(s = ctxt->allsym; s != nil; s = s->allsym) { if(!s->reachable || s->special || s->type != SRODATA) continue; if(strncmp(s->name, "type.", 5) == 0) { diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go index fbc6a3c698..0c58d628b4 100644 --- a/src/cmd/pack/pack_test.go +++ b/src/cmd/pack/pack_test.go @@ -257,15 +257,8 @@ func TestLargeDefs(t *testing.T) { } } - n := 10000 - if testing.Short() { - // Issue 9656: 10,000 is too aggressive for several - // builders, with ~120 MB of disk consumed. 1,000 is - // still enough to exercise the old bug. - n = 1000 - } printf("package large\n\ntype T struct {\n") - for i := 0; i < n; i++ { + for i := 0; i < 1000; i++ { printf("f%d int `tag:\"", i) for j := 0; j < 100; j++ { printf("t%d=%d,", j, j) diff --git a/src/cmd/pprof/internal/report/report.go b/src/cmd/pprof/internal/report/report.go index e5977fd034..586f41d4d6 100644 --- a/src/cmd/pprof/internal/report/report.go +++ b/src/cmd/pprof/internal/report/report.go @@ -1531,7 +1531,7 @@ func memoryLabel(value int64, fromUnit, toUnit string) (v float64, u string, ok output, toUnit = float64(value)/1024, "kB" case "mb", "mbyte", "megabyte": output, toUnit = float64(value)/(1024*1024), "MB" - case "gb", "gbyte", "giggabyte": + case "gb", "gbyte", "gigabyte": output, toUnit = float64(value)/(1024*1024*1024), "GB" } return output, toUnit, true diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go index 0a69c36afc..24692dc166 100644 --- a/src/cmd/yacc/yacc.go +++ b/src/cmd/yacc/yacc.go @@ -603,6 +603,7 @@ outer: } levprd[nprod] |= ACTFLAG fmt.Fprintf(fcode, "\n\tcase %v:", nprod) + fmt.Fprintf(fcode, "\n\t\t%sDollar = %sS[%spt-%v:%spt+1]", prefix, prefix, prefix, mem-1, prefix) cpyact(curprod, mem) // action within rule... @@ -1345,7 +1346,7 @@ loop: ungetrune(finput, c) continue loop } - fmt.Fprintf(fcode, "%sS[%spt-%v]", prefix, prefix, max-j-1) + fmt.Fprintf(fcode, "%sDollar[%v]", prefix, j) // put out the proper tag if ntypes != 0 { @@ -3264,6 +3265,7 @@ func $$Parse($$lex $$Lexer) int { var $$n int var $$lval $$SymType var $$VAL $$SymType + var $$Dollar []$$SymType $$S := make([]$$SymType, $$MaxDepth) Nerrs := 0 /* number of errors */ diff --git a/src/compress/bzip2/bzip2_test.go b/src/compress/bzip2/bzip2_test.go index fb79d089eb..77c50dfe94 100644 --- a/src/compress/bzip2/bzip2_test.go +++ b/src/compress/bzip2/bzip2_test.go @@ -200,7 +200,7 @@ func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) } func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) } func TestBufferOverrun(t *testing.T) { - // Tests https://code.google.com/p/go/issues/detail?id=5747. + // Tests https://golang.org/issue/5747. buffer := bytes.NewReader([]byte(bufferOverrunBase64)) decoder := base64.NewDecoder(base64.StdEncoding, buffer) decompressor := NewReader(decoder) @@ -209,7 +209,7 @@ func TestBufferOverrun(t *testing.T) { } func TestOutOfRangeSelector(t *testing.T) { - // Tests https://code.google.com/p/go/issues/detail?id=8363. + // Tests https://golang.org/issue/8363. buffer := bytes.NewReader(outOfRangeSelector) decompressor := NewReader(buffer) // This shouldn't panic. diff --git a/src/compress/flate/deflate_test.go b/src/compress/flate/deflate_test.go index 730234c385..53bfd26438 100644 --- a/src/compress/flate/deflate_test.go +++ b/src/compress/flate/deflate_test.go @@ -407,7 +407,7 @@ func TestWriterDict(t *testing.T) { } } -// See http://code.google.com/p/go/issues/detail?id=2508 +// See http://golang.org/issue/2508 func TestRegression2508(t *testing.T) { if testing.Short() { t.Logf("test disabled with -short") diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index a46133439d..c87da50df9 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -172,7 +172,7 @@ Curves: // Although sending an empty NPN extension is reasonable, Firefox has // had a bug around this. Best to send nothing at all if // config.NextProtos is empty. See - // https://code.google.com/p/go/issues/detail?id=5445. + // https://golang.org/issue/5445. if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 { hs.hello.nextProtoNeg = true hs.hello.nextProtos = config.NextProtos diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index ec1981423d..0181f140fa 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -323,6 +323,8 @@ nextIntermediate: } func matchHostnames(pattern, host string) bool { + host = strings.TrimSuffix(host, ".") + if len(pattern) == 0 || len(host) == 0 { return false } diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index bd7cbed8a2..45d49ce3e3 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -161,11 +161,16 @@ var matchHostnamesTests = []matchHostnamesTest{ {"", "b.b.c", false}, {"a.b.c", "", false}, {"example.com", "example.com", true}, + {"example.com", "example.com.", true}, {"example.com", "www.example.com", false}, {"*.example.com", "www.example.com", true}, + {"*.example.com", "www.example.com.", true}, {"*.example.com", "xyz.www.example.com", false}, {"*.*.example.com", "xyz.www.example.com", true}, {"*.www.*.com", "xyz.www.example.com", true}, + {"", ".", false}, + {".", "", false}, + {".", ".", false}, } func TestMatchHostnames(t *testing.T) { diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go index 8c6342013d..9d6045c916 100644 --- a/src/encoding/xml/marshal.go +++ b/src/encoding/xml/marshal.go @@ -179,17 +179,24 @@ var ( ) // EncodeToken writes the given XML token to the stream. -// It returns an error if StartElement and EndElement tokens are not properly matched. +// It returns an error if StartElement and EndElement tokens are not +// properly matched. // -// EncodeToken does not call Flush, because usually it is part of a larger operation -// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked -// during those), and those will call Flush when finished. -// Callers that create an Encoder and then invoke EncodeToken directly, without -// using Encode or EncodeElement, need to call Flush when finished to ensure -// that the XML is written to the underlying writer. +// EncodeToken does not call Flush, because usually it is part of a +// larger operation such as Encode or EncodeElement (or a custom +// Marshaler's MarshalXML invoked during those), and those will call +// Flush when finished. Callers that create an Encoder and then invoke +// EncodeToken directly, without using Encode or EncodeElement, need to +// call Flush when finished to ensure that the XML is written to the +// underlying writer. // -// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token -// in the stream. +// EncodeToken allows writing a ProcInst with Target set to "xml" only +// as the first token in the stream. +// +// When encoding a StartElement holding an XML namespace prefix +// declaration for a prefix that is not already declared, contained +// elements (including the StartElement itself) will use the declared +// prefix when encoding names with matching namespace URIs. func (enc *Encoder) EncodeToken(t Token) error { p := &enc.p switch t := t.(type) { @@ -256,19 +263,27 @@ type printer struct { depth int indentedIn bool putNewline bool + defaultNS string attrNS map[string]string // map prefix -> name space attrPrefix map[string]string // map name space -> prefix - prefixes []string + prefixes []printerPrefix tags []Name } -// createAttrPrefix finds the name space prefix attribute to use for the given name space, -// defining a new prefix if necessary. It returns the prefix. -func (p *printer) createAttrPrefix(url string) string { - if prefix := p.attrPrefix[url]; prefix != "" { - return prefix - } +// printerPrefix holds a namespace undo record. +// When an element is popped, the prefix record +// is set back to the recorded URL. The empty +// prefix records the URL for the default name space. +// +// The start of an element is recorded with an element +// that has mark=true. +type printerPrefix struct { + prefix string + url string + mark bool +} +func (p *printer) prefixForNS(url string, isAttr bool) string { // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml" // and must be referred to that way. // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns", @@ -276,12 +291,97 @@ func (p *printer) createAttrPrefix(url string) string { if url == xmlURL { return "xml" } + if !isAttr && url == p.defaultNS { + // We can use the default name space. + return "" + } + return p.attrPrefix[url] +} - // Need to define a new name space. - if p.attrPrefix == nil { - p.attrPrefix = make(map[string]string) - p.attrNS = make(map[string]string) +// defineNS pushes any namespace definition found in the given attribute. +// If ignoreNonEmptyDefault is true, an xmlns="nonempty" +// attribute will be ignored. +func (p *printer) defineNS(attr Attr, ignoreNonEmptyDefault bool) error { + var prefix string + if attr.Name.Local == "xmlns" { + if attr.Name.Space != "" && attr.Name.Space != "xml" && attr.Name.Space != xmlURL { + return fmt.Errorf("xml: cannot redefine xmlns attribute prefix") + } + } else if attr.Name.Space == "xmlns" && attr.Name.Local != "" { + prefix = attr.Name.Local + if attr.Value == "" { + // Technically, an empty XML namespace is allowed for an attribute. + // From http://www.w3.org/TR/xml-names11/#scoping-defaulting: + // + // The attribute value in a namespace declaration for a prefix may be + // empty. This has the effect, within the scope of the declaration, of removing + // any association of the prefix with a namespace name. + // + // However our namespace prefixes here are used only as hints. There's + // no need to respect the removal of a namespace prefix, so we ignore it. + return nil + } + } else { + // Ignore: it's not a namespace definition + return nil + } + if prefix == "" { + if attr.Value == p.defaultNS { + // No need for redefinition. + return nil + } + if attr.Value != "" && ignoreNonEmptyDefault { + // We have an xmlns="..." value but + // it can't define a name space in this context, + // probably because the element has an empty + // name space. In this case, we just ignore + // the name space declaration. + return nil + } + } else if _, ok := p.attrPrefix[attr.Value]; ok { + // There's already a prefix for the given name space, + // so use that. This prevents us from + // having two prefixes for the same name space + // so attrNS and attrPrefix can remain bijective. + return nil } + p.pushPrefix(prefix, attr.Value) + return nil +} + +// createNSPrefix creates a name space prefix attribute +// to use for the given name space, defining a new prefix +// if necessary. +// If isAttr is true, the prefix is to be created for an attribute +// prefix, which means that the default name space cannot +// be used. +func (p *printer) createNSPrefix(url string, isAttr bool) { + if _, ok := p.attrPrefix[url]; ok { + // We already have a prefix for the given URL. + return + } + switch { + case !isAttr && url == p.defaultNS: + // We can use the default name space. + return + case url == "": + // The only way we can encode names in the empty + // name space is by using the default name space, + // so we must use that. + if p.defaultNS != "" { + // The default namespace is non-empty, so we + // need to set it to empty. + p.pushPrefix("", "") + } + return + case url == xmlURL: + return + } + // TODO If the URL is an existing prefix, we could + // use it as is. That would enable the + // marshaling of elements that had been unmarshaled + // and with a name space prefix that was not found. + // although technically it would be incorrect. // Pick a name. We try to use the final element of the path // but fall back to _. @@ -306,39 +406,98 @@ func (p *printer) createAttrPrefix(url string) string { } } - p.attrPrefix[url] = prefix - p.attrNS[prefix] = url - - p.WriteString(`xmlns:`) - p.WriteString(prefix) - p.WriteString(`="`) - EscapeText(p, []byte(url)) - p.WriteString(`" `) + p.pushPrefix(prefix, url) +} - p.prefixes = append(p.prefixes, prefix) +// writeNamespaces writes xmlns attributes for all the +// namespace prefixes that have been defined in +// the current element. +func (p *printer) writeNamespaces() { + for i := len(p.prefixes) - 1; i >= 0; i-- { + prefix := p.prefixes[i] + if prefix.mark { + return + } + p.WriteString(" ") + if prefix.prefix == "" { + // Default name space. + p.WriteString(`xmlns="`) + } else { + p.WriteString("xmlns:") + p.WriteString(prefix.prefix) + p.WriteString(`="`) + } + EscapeText(p, []byte(p.nsForPrefix(prefix.prefix))) + p.WriteString(`"`) + } +} - return prefix +// pushPrefix pushes a new prefix on the prefix stack +// without checking to see if it is already defined. +func (p *printer) pushPrefix(prefix, url string) { + p.prefixes = append(p.prefixes, printerPrefix{ + prefix: prefix, + url: p.nsForPrefix(prefix), + }) + p.setAttrPrefix(prefix, url) } -// deleteAttrPrefix removes an attribute name space prefix. -func (p *printer) deleteAttrPrefix(prefix string) { - delete(p.attrPrefix, p.attrNS[prefix]) - delete(p.attrNS, prefix) +// nsForPrefix returns the name space for the given +// prefix. Note that this is not valid for the +// empty attribute prefix, which always has an empty +// name space. +func (p *printer) nsForPrefix(prefix string) string { + if prefix == "" { + return p.defaultNS + } + return p.attrNS[prefix] } +// markPrefix marks the start of an element on the prefix +// stack. func (p *printer) markPrefix() { - p.prefixes = append(p.prefixes, "") + p.prefixes = append(p.prefixes, printerPrefix{ + mark: true, + }) } +// popPrefix pops all defined prefixes for the current +// element. func (p *printer) popPrefix() { for len(p.prefixes) > 0 { prefix := p.prefixes[len(p.prefixes)-1] p.prefixes = p.prefixes[:len(p.prefixes)-1] - if prefix == "" { + if prefix.mark { break } - p.deleteAttrPrefix(prefix) + p.setAttrPrefix(prefix.prefix, prefix.url) + } +} + +// setAttrPrefix sets an attribute name space prefix. +// If url is empty, the attribute is removed. +// If prefix is empty, the default name space is set. +func (p *printer) setAttrPrefix(prefix, url string) { + if prefix == "" { + p.defaultNS = url + return + } + if url == "" { + delete(p.attrPrefix, p.attrNS[prefix]) + delete(p.attrNS, prefix) + return + } + if p.attrPrefix == nil { + // Need to define a new name space. + p.attrPrefix = make(map[string]string) + p.attrNS = make(map[string]string) } + // Remove any old prefix value. This is OK because we maintain a + // strict one-to-one mapping between prefix and URL (see + // defineNS) + delete(p.attrPrefix, p.attrNS[prefix]) + p.attrPrefix[url] = prefix + p.attrNS[prefix] = url } var ( @@ -376,23 +535,23 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat // Check for marshaler. if val.CanInterface() && typ.Implements(marshalerType) { - return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate)) + return p.marshalInterface(val.Interface().(Marshaler), p.defaultStart(typ, finfo, startTemplate)) } if val.CanAddr() { pv := val.Addr() if pv.CanInterface() && pv.Type().Implements(marshalerType) { - return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate)) + return p.marshalInterface(pv.Interface().(Marshaler), p.defaultStart(pv.Type(), finfo, startTemplate)) } } // Check for text marshaler. if val.CanInterface() && typ.Implements(textMarshalerType) { - return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate)) + return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), p.defaultStart(typ, finfo, startTemplate)) } if val.CanAddr() { pv := val.Addr() if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { - return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate)) + return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), p.defaultStart(pv.Type(), finfo, startTemplate)) } } @@ -419,6 +578,10 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat // 3. type name var start StartElement + // Historic behaviour: elements use the default name space + // they are contained in by default. + start.Name.Space = p.defaultNS + if startTemplate != nil { start.Name = startTemplate.Name start.Attr = append(start.Attr, startTemplate.Attr...) @@ -431,7 +594,10 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat } } if start.Name.Local == "" && finfo != nil { - start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name + start.Name.Local = finfo.name + if finfo.xmlns != "" { + start.Name.Space = finfo.xmlns + } } if start.Name.Local == "" { name := typ.Name() @@ -440,6 +606,9 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat } start.Name.Local = name } + // Historic behaviour: an element that's in a namespace sets + // the default namespace for all elements contained within it. + start.setDefaultNamespace() // Attributes for i := range tinfo.fields { @@ -552,7 +721,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat // defaultStart returns the default start element to use, // given the reflect type, field info, and start template. -func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement { +func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement { var start StartElement // Precedence for the XML element name is as above, // except that we do not look inside structs for the first field. @@ -569,6 +738,12 @@ func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElemen // since it has the Marshaler methods. start.Name.Local = typ.Elem().Name() } + // Historic behaviour: elements use the name space of + // the element they are contained in by default. + if start.Name.Space == "" { + start.Name.Space = p.defaultNS + } + start.setDefaultNamespace() return start } @@ -613,29 +788,44 @@ func (p *printer) writeStart(start *StartElement) error { p.tags = append(p.tags, start.Name) p.markPrefix() + // Define any name spaces explicitly declared in the attributes. + // We do this as a separate pass so that explicitly declared prefixes + // will take precedence over implicitly declared prefixes + // regardless of the order of the attributes. + ignoreNonEmptyDefault := start.Name.Space == "" + for _, attr := range start.Attr { + if err := p.defineNS(attr, ignoreNonEmptyDefault); err != nil { + return err + } + } + // Define any new name spaces implied by the attributes. + for _, attr := range start.Attr { + name := attr.Name + // From http://www.w3.org/TR/xml-names11/#defaulting + // "Default namespace declarations do not apply directly + // to attribute names; the interpretation of unprefixed + // attributes is determined by the element on which they + // appear." + // This means we don't need to create a new namespace + // when an attribute name space is empty. + if name.Space != "" && !name.isNamespace() { + p.createNSPrefix(name.Space, true) + } + } + p.createNSPrefix(start.Name.Space, false) p.writeIndent(1) p.WriteByte('<') - p.WriteString(start.Name.Local) - - if start.Name.Space != "" { - p.WriteString(` xmlns="`) - p.EscapeString(start.Name.Space) - p.WriteByte('"') - } - - // Attributes + p.writeName(start.Name, false) + p.writeNamespaces() for _, attr := range start.Attr { name := attr.Name - if name.Local == "" { + if name.Local == "" || name.isNamespace() { + // Namespaces have already been written by writeNamespaces above. continue } p.WriteByte(' ') - if name.Space != "" { - p.WriteString(p.createAttrPrefix(name.Space)) - p.WriteByte(':') - } - p.WriteString(name.Local) + p.writeName(name, true) p.WriteString(`="`) p.EscapeString(attr.Value) p.WriteByte('"') @@ -644,6 +834,16 @@ func (p *printer) writeStart(start *StartElement) error { return nil } +// writeName writes the given name. It assumes +// that p.createNSPrefix(name) has already been called. +func (p *printer) writeName(name Name, isAttr bool) { + if prefix := p.prefixForNS(name.Space, isAttr); prefix != "" { + p.WriteString(prefix) + p.WriteByte(':') + } + p.WriteString(name.Local) +} + func (p *printer) writeEnd(name Name) error { if name.Local == "" { return fmt.Errorf("xml: end tag with no name") @@ -662,7 +862,7 @@ func (p *printer) writeEnd(name Name) error { p.writeIndent(-1) p.WriteByte('<') p.WriteByte('/') - p.WriteString(name.Local) + p.writeName(name, false) p.WriteByte('>') p.popPrefix() return nil diff --git a/src/encoding/xml/marshal_test.go b/src/encoding/xml/marshal_test.go index cdd52ff97f..cc6994338d 100644 --- a/src/encoding/xml/marshal_test.go +++ b/src/encoding/xml/marshal_test.go @@ -1194,41 +1194,363 @@ func TestStructPointerMarshal(t *testing.T) { } var encodeTokenTests = []struct { - tok Token + desc string + toks []Token want string - ok bool -}{ - {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true}, - {StartElement{Name{"space", ""}, nil}, "", false}, - {EndElement{Name{"space", ""}}, "", false}, - {CharData("foo"), "foo", true}, - {Comment("foo"), "<!--foo-->", true}, - {Comment("foo-->"), "", false}, - {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true}, - {ProcInst{"", []byte("Instruction")}, "", false}, - {ProcInst{"Target", []byte("Instruction?>")}, "", false}, - {Directive("foo"), "<!foo>", true}, - {Directive("foo>"), "", false}, -} + err string +}{{ + desc: "start element with name space", + toks: []Token{ + StartElement{Name{"space", "local"}, nil}, + }, + want: `<space:local xmlns:space="space">`, +}, { + desc: "start element with no name", + toks: []Token{ + StartElement{Name{"space", ""}, nil}, + }, + err: "xml: start tag with no name", +}, { + desc: "end element with no name", + toks: []Token{ + EndElement{Name{"space", ""}}, + }, + err: "xml: end tag with no name", +}, { + desc: "char data", + toks: []Token{ + CharData("foo"), + }, + want: `foo`, +}, { + desc: "char data with escaped chars", + toks: []Token{ + CharData(" \t\n"), + }, + want: ` 	
`, +}, { + desc: "comment", + toks: []Token{ + Comment("foo"), + }, + want: `<!--foo-->`, +}, { + desc: "comment with invalid content", + toks: []Token{ + Comment("foo-->"), + }, + err: "xml: EncodeToken of Comment containing --> marker", +}, { + desc: "proc instruction", + toks: []Token{ + ProcInst{"Target", []byte("Instruction")}, + }, + want: `<?Target Instruction?>`, +}, { + desc: "proc instruction with empty target", + toks: []Token{ + ProcInst{"", []byte("Instruction")}, + }, + err: "xml: EncodeToken of ProcInst with invalid Target", +}, { + desc: "proc instruction with bad content", + toks: []Token{ + ProcInst{"", []byte("Instruction?>")}, + }, + err: "xml: EncodeToken of ProcInst with invalid Target", +}, { + desc: "directive", + toks: []Token{ + Directive("foo"), + }, + want: `<!foo>`, +}, { + desc: "directive instruction with bad name", + toks: []Token{ + Directive("foo>"), + }, + err: "xml: EncodeToken of Directive containing > marker", +}, { + desc: "end tag without start tag", + toks: []Token{ + EndElement{Name{"foo", "bar"}}, + }, + err: "xml: end tag </bar> without start tag", +}, { + desc: "mismatching end tag local name", + toks: []Token{ + StartElement{Name{"", "foo"}, nil}, + EndElement{Name{"", "bar"}}, + }, + err: "xml: end tag </bar> does not match start tag <foo>", + want: `<foo>`, +}, { + desc: "mismatching end tag namespace", + toks: []Token{ + StartElement{Name{"space", "foo"}, nil}, + EndElement{Name{"another", "foo"}}, + }, + err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space", + want: `<space:foo xmlns:space="space">`, +}, { + desc: "start element with explicit namespace", + toks: []Token{ + StartElement{Name{"space", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + {Name{"space", "foo"}, "value"}, + }}, + }, + want: `<x:local xmlns:x="space" x:foo="value">`, +}, { + desc: "start element with explicit namespace and colliding prefix", + toks: []Token{ + StartElement{Name{"space", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + {Name{"space", "foo"}, "value"}, + {Name{"x", "bar"}, "other"}, + }}, + }, + want: `<x:local xmlns:x_1="x" xmlns:x="space" x:foo="value" x_1:bar="other">`, +}, { + desc: "start element using previously defined namespace", + toks: []Token{ + StartElement{Name{"", "local"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"space", "x"}, "y"}, + }}, + }, + want: `<local xmlns:x="space"><x:foo x:x="y">`, +}, { + desc: "nested name space with same prefix", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space1"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space2"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"space1", "a"}, "space1 value"}, + {Name{"space2", "b"}, "space2 value"}, + }}, + EndElement{Name{"", "foo"}}, + EndElement{Name{"", "foo"}}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"space1", "a"}, "space1 value"}, + {Name{"space2", "b"}, "space2 value"}, + }}, + }, + want: `<foo xmlns:x="space1"><foo xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" x:b="space2 value"></foo></foo><foo xmlns:space2="space2" x:a="space1 value" space2:b="space2 value">`, +}, { + desc: "start element defining several prefixes for the same name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "a"}, "space"}, + {Name{"xmlns", "b"}, "space"}, + {Name{"space", "x"}, "value"}, + }}, + }, + want: `<a:foo xmlns:a="space" a:x="value">`, +}, { + desc: "nested element redefines name space", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "y"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns:x="space"><x:foo x:a="value">`, +}, { + desc: "nested element creates alias for default name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xmlns", "y"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns="space"><foo xmlns:y="space" y:a="value">`, +}, { + desc: "nested element defines default name space with existing prefix", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "x"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"space", "a"}, "value"}, + }}, + }, + want: `<foo xmlns:x="space"><foo xmlns="space" x:a="value">`, +}, { + desc: "nested element uses empty attribute name space when default ns defined", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "attr"}, "value"}, + }}, + }, + want: `<foo xmlns="space"><foo attr="value">`, +}, { + desc: "redefine xmlns", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"foo", "xmlns"}, "space"}, + }}, + }, + err: `xml: cannot redefine xmlns attribute prefix`, +}, { + desc: "xmlns with explicit name space #1", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"xml", "xmlns"}, "space"}, + }}, + }, + want: `<foo xmlns="space">`, +}, { + desc: "xmlns with explicit name space #2", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{xmlURL, "xmlns"}, "space"}, + }}, + }, + want: `<foo xmlns="space">`, +}, { + desc: "empty name space declaration is ignored", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"xmlns", "foo"}, ""}, + }}, + }, + want: `<foo>`, +}, { + desc: "attribute with no name is ignored", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", ""}, "value"}, + }}, + }, + want: `<foo>`, +}, { + desc: "namespace URL with non-valid name", + toks: []Token{ + StartElement{Name{"/34", "foo"}, []Attr{ + {Name{"/34", "x"}, "value"}, + }}, + }, + want: `<_:foo xmlns:_="/34" _:x="value">`, +}, { + desc: "nested element resets default namespace to empty", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", "xmlns"}, ""}, + {Name{"", "x"}, "value"}, + {Name{"space", "x"}, "value"}, + }}, + }, + want: `<foo xmlns="space"><foo xmlns:space="space" xmlns="" x="value" space:x="value">`, +}, { + desc: "nested element requires empty default name space", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"", "foo"}, nil}, + }, + want: `<foo xmlns="space"><foo xmlns="">`, +}, { + desc: "attribute uses name space from xmlns", + toks: []Token{ + StartElement{Name{"some/space", "foo"}, []Attr{ + {Name{"", "attr"}, "value"}, + {Name{"some/space", "other"}, "other value"}, + }}, + }, + want: `<space:foo xmlns:space="some/space" attr="value" space:other="other value">`, +}, { + desc: "default name space should not be used by attributes", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"xmlns", "bar"}, "space"}, + {Name{"space", "baz"}, "foo"}, + }}, + StartElement{Name{"space", "baz"}, nil}, + EndElement{Name{"space", "baz"}}, + EndElement{Name{"space", "foo"}}, + }, + want: `<foo xmlns:bar="space" xmlns="space" bar:baz="foo"><baz></baz></foo>`, +}, { + desc: "default name space not used by attributes, not explicitly defined", + toks: []Token{ + StartElement{Name{"space", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + {Name{"space", "baz"}, "foo"}, + }}, + StartElement{Name{"space", "baz"}, nil}, + EndElement{Name{"space", "baz"}}, + EndElement{Name{"space", "foo"}}, + }, + want: `<foo xmlns:space="space" xmlns="space" space:baz="foo"><baz></baz></foo>`, +}, { + desc: "impossible xmlns declaration", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"", "xmlns"}, "space"}, + }}, + StartElement{Name{"space", "bar"}, []Attr{ + {Name{"space", "attr"}, "value"}, + }}, + }, + want: `<foo><space:bar xmlns:space="space" space:attr="value">`, +}} func TestEncodeToken(t *testing.T) { - for _, tt := range encodeTokenTests { +loop: + for i, tt := range encodeTokenTests { var buf bytes.Buffer enc := NewEncoder(&buf) - err := enc.EncodeToken(tt.tok) + var err error + for j, tok := range tt.toks { + err = enc.EncodeToken(tok) + if err != nil && j < len(tt.toks)-1 { + t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err) + continue loop + } + } + errorf := func(f string, a ...interface{}) { + t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...)) + } switch { - case !tt.ok && err == nil: - t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok) - case tt.ok && err != nil: - t.Fatalf("enc.EncodeToken: %v", err) - case !tt.ok && err != nil: - // expected error, got one + case tt.err != "" && err == nil: + errorf(" expected error; got none") + continue + case tt.err == "" && err != nil: + errorf(" got error: %v", err) + continue + case tt.err != "" && err != nil && tt.err != err.Error(): + errorf(" error mismatch; got %v, want %v", err, tt.err) + continue } if err := enc.Flush(); err != nil { - t.Fatalf("enc.EncodeToken: %v", err) + errorf(" %v", err) + continue } if got := buf.String(); got != tt.want { - t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want) + errorf("\ngot %v\nwant %v", got, tt.want) + continue } } } diff --git a/src/encoding/xml/read_test.go b/src/encoding/xml/read_test.go index 01f55d0dd0..02f1e10c33 100644 --- a/src/encoding/xml/read_test.go +++ b/src/encoding/xml/read_test.go @@ -5,6 +5,8 @@ package xml import ( + "bytes" + "fmt" "io" "reflect" "strings" @@ -484,6 +486,34 @@ func TestUnmarshalNS(t *testing.T) { } } +func TestRoundTrip(t *testing.T) { + // From issue 7535 + const s = `<ex:element xmlns:ex="http://example.com/schema"></ex:element>` + in := bytes.NewBufferString(s) + for i := 0; i < 10; i++ { + out := &bytes.Buffer{} + d := NewDecoder(in) + e := NewEncoder(out) + + for { + t, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + fmt.Println("failed:", err) + return + } + e.EncodeToken(t) + } + e.Flush() + in = out + } + if got := in.String(); got != s { + t.Errorf("have: %q\nwant: %q\n", got, s) + } +} + func TestMarshalNS(t *testing.T) { dst := Tables{"hello", "world"} data, err := Marshal(&dst) @@ -607,7 +637,7 @@ func TestMarshalNSAttr(t *testing.T) { if err != nil { t.Fatalf("Marshal: %v", err) } - want := `<TableAttrs><TAttr xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" xmlns:furniture="http://www.w3schools.com/furniture" furniture:table="world" xml:lang="en_US" xmlns:_xml="http://golang.org/xml/" _xml:other="other1" xmlns:_xmlfoo="http://golang.org/xmlfoo/" _xmlfoo:other="other2" xmlns:json="http://golang.org/json/" json:other="other3" xmlns:json_1="http://golang.org/2/json/" json_1:other="other4"></TAttr></TableAttrs>` + want := `<TableAttrs><TAttr xmlns:json_1="http://golang.org/2/json/" xmlns:json="http://golang.org/json/" xmlns:_xmlfoo="http://golang.org/xmlfoo/" xmlns:_xml="http://golang.org/xml/" xmlns:furniture="http://www.w3schools.com/furniture" xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" furniture:table="world" xml:lang="en_US" _xml:other="other1" _xmlfoo:other="other2" json:other="other3" json_1:other="other4"></TAttr></TableAttrs>` str := string(data) if str != want { t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want) @@ -694,7 +724,7 @@ type Pod struct { Pea interface{} `xml:"Pea"` } -// https://code.google.com/p/go/issues/detail?id=6836 +// https://golang.org/issue/6836 func TestUnmarshalIntoInterface(t *testing.T) { pod := new(Pod) pod.Pea = new(Pea) diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go index 5690b20256..0c64cd730d 100644 --- a/src/encoding/xml/xml.go +++ b/src/encoding/xml/xml.go @@ -35,15 +35,24 @@ func (e *SyntaxError) Error() string { return "XML syntax error on line " + strconv.Itoa(e.Line) + ": " + e.Msg } -// A Name represents an XML name (Local) annotated -// with a name space identifier (Space). -// In tokens returned by Decoder.Token, the Space identifier -// is given as a canonical URL, not the short prefix used -// in the document being parsed. +// A Name represents an XML name (Local) annotated with a name space +// identifier (Space). In tokens returned by Decoder.Token, the Space +// identifier is given as a canonical URL, not the short prefix used in +// the document being parsed. +// +// As a special case, XML namespace declarations will use the literal +// string "xmlns" for the Space field instead of the fully resolved URL. +// See Encoder.EncodeToken for more information on namespace encoding +// behaviour. type Name struct { Space, Local string } +// isNamespace reports whether the name is a namespace-defining name. +func (name Name) isNamespace() bool { + return name.Local == "xmlns" || name.Space == "xmlns" +} + // An Attr represents an attribute in an XML element (Name=Value). type Attr struct { Name Name @@ -72,6 +81,24 @@ func (e StartElement) End() EndElement { return EndElement{e.Name} } +// setDefaultNamespace sets the namespace of the element +// as the default for all elements contained within it. +func (e *StartElement) setDefaultNamespace() { + if e.Name.Space == "" { + // If there's no namespace on the element, don't + // set the default. Strictly speaking this might be wrong, as + // we can't tell if the element had no namespace set + // or was just using the default namespace. + return + } + e.Attr = append(e.Attr, Attr{ + Name: Name{ + Local: "xmlns", + }, + Value: e.Name.Space, + }) +} + // An EndElement represents an XML end element. type EndElement struct { Name Name @@ -723,7 +750,7 @@ func (d *Decoder) rawToken() (Token, error) { return nil, d.err } - attr = make([]Attr, 0, 4) + attr = []Attr{} for { d.space() if b, ok = d.mustgetc(); !ok { @@ -747,7 +774,11 @@ func (d *Decoder) rawToken() (Token, error) { n := len(attr) if n >= cap(attr) { - nattr := make([]Attr, n, 2*cap(attr)) + nCap := 2 * cap(attr) + if nCap == 0 { + nCap = 4 + } + nattr := make([]Attr, n, nCap) copy(nattr, attr) attr = nattr } diff --git a/src/expvar/expvar_test.go b/src/expvar/expvar_test.go index 765e3b757e..11e6497b96 100644 --- a/src/expvar/expvar_test.go +++ b/src/expvar/expvar_test.go @@ -7,8 +7,11 @@ package expvar import ( "bytes" "encoding/json" + "net" "net/http/httptest" + "runtime" "strconv" + "sync" "testing" ) @@ -47,6 +50,26 @@ func TestInt(t *testing.T) { } } +func BenchmarkIntAdd(b *testing.B) { + var v Int + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v.Add(1) + } + }) +} + +func BenchmarkIntSet(b *testing.B) { + var v Int + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + v.Set(1) + } + }) +} + func TestFloat(t *testing.T) { RemoveAll() reqs := NewFloat("requests-float") @@ -73,6 +96,26 @@ func TestFloat(t *testing.T) { } } +func BenchmarkFloatAdd(b *testing.B) { + var f Float + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Add(1.0) + } + }) +} + +func BenchmarkFloatSet(b *testing.B) { + var f Float + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + f.Set(1.0) + } + }) +} + func TestString(t *testing.T) { RemoveAll() name := NewString("my-name") @@ -90,6 +133,16 @@ func TestString(t *testing.T) { } } +func BenchmarkStringSet(b *testing.B) { + var s String + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + s.Set("red") + } + }) +} + func TestMapCounter(t *testing.T) { RemoveAll() colors := NewMap("bike-shed-colors") @@ -130,6 +183,38 @@ func TestMapCounter(t *testing.T) { } } +func BenchmarkMapSet(b *testing.B) { + m := new(Map).Init() + + v := new(Int) + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + m.Set("red", v) + } + }) +} + +func BenchmarkMapAddSame(b *testing.B) { + for i := 0; i < b.N; i++ { + m := new(Map).Init() + m.Add("red", 1) + m.Add("red", 1) + m.Add("red", 1) + m.Add("red", 1) + } +} + +func BenchmarkMapAddDifferent(b *testing.B) { + for i := 0; i < b.N; i++ { + m := new(Map).Init() + m.Add("red", 1) + m.Add("blue", 1) + m.Add("green", 1) + m.Add("yellow", 1) + } +} + func TestFunc(t *testing.T) { RemoveAll() var x interface{} = []string{"a", "b"} @@ -165,3 +250,135 @@ func TestHandler(t *testing.T) { t.Errorf("HTTP handler wrote:\n%s\nWant:\n%s", got, want) } } + +func BenchmarkRealworldExpvarUsage(b *testing.B) { + var ( + bytesSent Int + bytesRead Int + ) + + // The benchmark creates GOMAXPROCS client/server pairs. + // Each pair creates 4 goroutines: client reader/writer and server reader/writer. + // The benchmark stresses concurrent reading and writing to the same connection. + // Such pattern is used in net/http and net/rpc. + + b.StopTimer() + + P := runtime.GOMAXPROCS(0) + N := b.N / P + W := 1000 + + // Setup P client/server connections. + clients := make([]net.Conn, P) + servers := make([]net.Conn, P) + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + b.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + done := make(chan bool) + go func() { + for p := 0; p < P; p++ { + s, err := ln.Accept() + if err != nil { + b.Errorf("Accept failed: %v", err) + return + } + servers[p] = s + } + done <- true + }() + for p := 0; p < P; p++ { + c, err := net.Dial("tcp", ln.Addr().String()) + if err != nil { + b.Fatalf("Dial failed: %v", err) + } + clients[p] = c + } + <-done + + b.StartTimer() + + var wg sync.WaitGroup + wg.Add(4 * P) + for p := 0; p < P; p++ { + // Client writer. + go func(c net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := byte(i) + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + n, err := c.Write(buf[:]) + if err != nil { + b.Errorf("Write failed: %v", err) + return + } + + bytesSent.Add(int64(n)) + } + }(clients[p]) + + // Pipe between server reader and server writer. + pipe := make(chan byte, 128) + + // Server reader. + go func(s net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + n, err := s.Read(buf[:]) + + if err != nil { + b.Errorf("Read failed: %v", err) + return + } + + bytesRead.Add(int64(n)) + pipe <- buf[0] + } + }(servers[p]) + + // Server writer. + go func(s net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := <-pipe + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + n, err := s.Write(buf[:]) + if err != nil { + b.Errorf("Write failed: %v", err) + return + } + + bytesSent.Add(int64(n)) + } + s.Close() + }(servers[p]) + + // Client reader. + go func(c net.Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + n, err := c.Read(buf[:]) + + if err != nil { + b.Errorf("Read failed: %v", err) + return + } + + bytesRead.Add(int64(n)) + } + c.Close() + }(clients[p]) + } + wg.Wait() +} diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index b3c1105156..98201a5d96 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -240,7 +240,7 @@ var pkgDeps = map[string][]string{ // Basic networking. // Because net must be used by any package that wants to // do networking portably, it must have a small dependency set: just L1+basic os. - "net": {"L1", "CGO", "os", "syscall", "time"}, + "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows"}, // NET enables use of basic network-related packages. "NET": { diff --git a/src/html/template/clone_test.go b/src/html/template/clone_test.go index e11bff2c5d..5de3bc0eef 100644 --- a/src/html/template/clone_test.go +++ b/src/html/template/clone_test.go @@ -166,7 +166,7 @@ func TestCloneThenParse(t *testing.T) { } } -// https://code.google.com/p/go/issues/detail?id=5980 +// https://golang.org/issue/5980 func TestFuncMapWorksAfterClone(t *testing.T) { funcs := FuncMap{"customFunc": func() (string, error) { return "", errors.New("issue5980") diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go index 5f3ffe2d32..e698328693 100644 --- a/src/html/template/content_test.go +++ b/src/html/template/content_test.go @@ -260,7 +260,7 @@ func TestStringer(t *testing.T) { } } -// https://code.google.com/p/go/issues/detail?id=5982 +// https://golang.org/issue/5982 func TestEscapingNilNonemptyInterfaces(t *testing.T) { tmpl := Must(New("x").Parse("{{.E}}")) diff --git a/src/image/image.go b/src/image/image.go index e0ecd92baa..951cc8ae05 100644 --- a/src/image/image.go +++ b/src/image/image.go @@ -46,9 +46,9 @@ type Image interface { } // PalettedImage is an image whose colors may come from a limited palette. -// If m is a PalettedImage and m.ColorModel() returns a PalettedColorModel p, +// If m is a PalettedImage and m.ColorModel() returns a color.Palette p, // then m.At(x, y) should be equivalent to p[m.ColorIndexAt(x, y)]. If m's -// color model is not a PalettedColorModel, then ColorIndexAt's behavior is +// color model is not a color.Palette, then ColorIndexAt's behavior is // undefined. type PalettedImage interface { // ColorIndexAt returns the palette index of the pixel at (x, y). diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go new file mode 100644 index 0000000000..2541a83440 --- /dev/null +++ b/src/internal/syscall/windows/syscall_windows.go @@ -0,0 +1,99 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package windows + +import ( + "syscall" +) + +//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go + +const GAA_FLAG_INCLUDE_PREFIX = 0x00000010 + +const IF_TYPE_SOFTWARE_LOOPBACK = 24 + +type SocketAddress struct { + Sockaddr *syscall.RawSockaddrAny + SockaddrLength int32 +} + +type IpAdapterUnicastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterUnicastAddress + Address SocketAddress + PrefixOrigin int32 + SuffixOrigin int32 + DadState int32 + ValidLifetime uint32 + PreferredLifetime uint32 + LeaseLifetime uint32 + OnLinkPrefixLength uint8 +} + +type IpAdapterAnycastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterAnycastAddress + Address SocketAddress +} + +type IpAdapterMulticastAddress struct { + Length uint32 + Flags uint32 + Next *IpAdapterMulticastAddress + Address SocketAddress +} + +type IpAdapterDnsServerAdapter struct { + Length uint32 + Reserved uint32 + Next *IpAdapterDnsServerAdapter + Address SocketAddress +} + +type IpAdapterPrefix struct { + Length uint32 + Flags uint32 + Next *IpAdapterPrefix + Address SocketAddress + PrefixLength uint32 +} + +type IpAdapterAddresses struct { + Length uint32 + IfIndex uint32 + Next *IpAdapterAddresses + AdapterName *byte + FirstUnicastAddress *IpAdapterUnicastAddress + FirstAnycastAddress *IpAdapterAnycastAddress + FirstMulticastAddress *IpAdapterMulticastAddress + FirstDnsServerAddress *IpAdapterDnsServerAdapter + DnsSuffix *uint16 + Description *uint16 + FriendlyName *uint16 + PhysicalAddress [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte + PhysicalAddressLength uint32 + Flags uint32 + Mtu uint32 + IfType uint32 + OperStatus uint32 + Ipv6IfIndex uint32 + ZoneIndices [16]uint32 + FirstPrefix *IpAdapterPrefix + /* more fields might be present here. */ +} + +const ( + IfOperStatusUp = 1 + IfOperStatusDown = 2 + IfOperStatusTesting = 3 + IfOperStatusUnknown = 4 + IfOperStatusDormant = 5 + IfOperStatusNotPresent = 6 + IfOperStatusLowerLayerDown = 7 +) + +//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go new file mode 100644 index 0000000000..90e2034641 --- /dev/null +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -0,0 +1,20 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package windows + +import "unsafe" +import "syscall" + +var ( + modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll") + + procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") +) + +func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) { + r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizeOfPointer)), 0) + if r0 != 0 { + errcode = syscall.Errno(r0) + } + return +} diff --git a/src/lib9/math_darwin_arm.c b/src/lib9/math_darwin_arm.c new file mode 100644 index 0000000000..ff5b0ce07a --- /dev/null +++ b/src/lib9/math_darwin_arm.c @@ -0,0 +1,102 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Some Darwin/ARM libc versions fail to provide a standard compliant version +// of frexp and ldexp that could handle denormal floating point numbers. +// The frexp and ldexp implementations are translated from their Go version. + +#include <stdint.h> + +// Assume double and uint64_t are using the same endian. +union dint64 { + double d; + uint64_t u; +}; + +static const uint64_t mask = 0x7FF, bias = 1023; +static const int shift = 64 - 11 - 1; +static const uint64_t uvnan = 0x7FF8000000000001ULL, uvinf = 0x7FF0000000000000ULL, + uvneginf = 0xFFF0000000000000ULL; +static const double smallestnormal = 2.2250738585072014e-308; // 2**-1022 + +static inline uint64_t float64bits(double x) { + union dint64 u; + u.d = x; + return u.u; +} +static inline double float64frombits(uint64_t x) { + union dint64 u; + u.u = x; + return u.d; +} +static inline int isinf(double x) { + return float64bits(x) == uvinf || float64bits(x) == uvneginf; +} +static inline int isnan(double x) { + return x != x; +} +extern double fabs(double); +static double normalize(double x, int *exp) { + if (fabs(x) < smallestnormal) { + *exp = -52; + return x * (double)(1LL<<52); + } + *exp = 0; + return x; +} + +double ldexp(double frac, int exp) { + // special cases + if (frac == 0.0) return frac; + if (isinf(frac) || isnan(frac)) return frac; + + int e; + frac = normalize(frac, &e); + exp += e; + uint64_t x = float64bits(frac); + exp += (int)((x>>shift)&mask) - bias; + if (exp < -1074) { // underflow + if (frac < 0.0) return float64frombits(1ULL<<63); // -0.0 + return 0.0; + } + if (exp > 1023) { // overflow + if (frac < 0.0) return float64frombits(uvneginf); + return float64frombits(uvinf); + } + double m = 1; + if (exp < -1022) { // denormal + exp += 52; + m = 1.0 / (double)(1ULL<<52); + } + x &= ~(mask << shift); + x |= (uint64_t)(exp+bias) << shift; + return m * float64frombits(x); +} + +double frexp(double f, int *exp) { + *exp = 0; + // special cases + if (f == 0.0) return f; + if (isinf(f) || isnan(f)) return f; + + f = normalize(f, exp); + uint64_t x = float64bits(f); + *exp += (int)((x>>shift)&mask) - bias + 1; + x &= ~(mask << shift); + x |= (-1 + bias) << shift; + return float64frombits(x); +} + +// On Darwin/ARM, the kernel insists on running VFP in runfast mode, and it +// cannot deal with denormal floating point numbers in that mode, so we have +// to disable the runfast mode if the client uses ldexp/frexp (i.e. 5g). +void disable_vfp_runfast(void) __attribute__((constructor)); +void disable_vfp_runfast(void) { + __asm__ volatile ( + "fmrx r0, fpscr\n" + "bic r0, r0, $0x03000000\n" + "fmxr fpscr, r0\n" + : : : "r0" + ); +} diff --git a/src/liblink/asm5.c b/src/liblink/asm5.c index 8d597750b7..398b7841c2 100644 --- a/src/liblink/asm5.c +++ b/src/liblink/asm5.c @@ -96,6 +96,7 @@ static Optab optab[] = { ABL, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, { ABX, C_NONE, C_NONE, C_SBRA, 74, 20, 0 }, { ABEQ, C_NONE, C_NONE, C_SBRA, 5, 4, 0 }, + { ABEQ, C_RCON, C_NONE, C_SBRA, 5, 4, 0 }, // prediction hinted form, hint ignored { AB, C_NONE, C_NONE, C_ROREG, 6, 4, 0, LPOOL }, { ABL, C_NONE, C_NONE, C_ROREG, 7, 4, 0 }, @@ -353,14 +354,6 @@ static uchar xcmp[C_GOK+1][C_GOK+1]; static LSym *deferreturn; -static void -nocache(Prog *p) -{ - p->optab = 0; - p->from.class = 0; - p->to.class = 0; -} - /* size of a case statement including jump table */ static int32 casesz(Link *ctxt, Prog *p) @@ -1638,8 +1631,11 @@ if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->na // runtime.tlsg is special. // Its "address" is the offset from the TLS thread pointer // to the thread-local g and m pointers. - // Emit a TLS relocation instead of a standard one. - if(rel->sym == ctxt->tlsg) { + // Emit a TLS relocation instead of a standard one if its + // type is not explicitly set by runtime. This assumes that + // all references to runtime.tlsg should be accompanied with + // its type declaration if necessary. + if(rel->sym == ctxt->tlsg && ctxt->tlsg->type == 0) { rel->type = R_TLS; if(ctxt->flag_shared) rel->add += ctxt->pc - p->pcrel->pc - 8 - rel->siz; diff --git a/src/liblink/go.c b/src/liblink/go.c index 754a7cc15e..08ce82b30c 100644 --- a/src/liblink/go.c +++ b/src/liblink/go.c @@ -133,3 +133,24 @@ double2ieee(uint64 *ieee, float64 f) { memmove(ieee, &f, 8); } + +void +nopout(Prog *p) +{ + p->as = ANOP; + p->scond = zprog.scond; + p->from = zprog.from; + p->from3 = zprog.from3; + p->reg = zprog.reg; + p->to = zprog.to; +} + +void +nocache(Prog *p) +{ + p->optab = 0; + p->from.class = 0; + p->from3.class = 0; + p->to.class = 0; +} + diff --git a/src/liblink/list5.c b/src/liblink/list5.c index bca10f6899..3ea411d2b8 100644 --- a/src/liblink/list5.c +++ b/src/liblink/list5.c @@ -168,7 +168,7 @@ Dconv(Fmt *fp) if(a->u.argsize == ArgsSizeUnknown) sprint(str, "$%lld", a->offset); else - sprint(str, "$%lld-%lld", a->offset, a->u.argsize); + sprint(str, "$%lld-%d", a->offset, a->u.argsize); break; case TYPE_SHIFT: diff --git a/src/liblink/list6.c b/src/liblink/list6.c index d9e0b45c7a..b324ec89d6 100644 --- a/src/liblink/list6.c +++ b/src/liblink/list6.c @@ -206,7 +206,7 @@ Dconv(Fmt *fp) if(a->u.argsize == ArgsSizeUnknown) sprint(str, "$%lld", a->offset); else - sprint(str, "$%lld-%lld", a->offset, a->u.argsize); + sprint(str, "$%lld-%d", a->offset, a->u.argsize); break; case TYPE_FCONST: diff --git a/src/liblink/list8.c b/src/liblink/list8.c index dbba103280..07c1d7cd67 100644 --- a/src/liblink/list8.c +++ b/src/liblink/list8.c @@ -193,7 +193,7 @@ Dconv(Fmt *fp) if(a->u.argsize == ArgsSizeUnknown) sprint(str, "$%lld", a->offset); else - sprint(str, "$%lld-%lld", a->offset, a->u.argsize); + sprint(str, "$%lld-%d", a->offset, a->u.argsize); break; case TYPE_FCONST: diff --git a/src/liblink/list9.c b/src/liblink/list9.c index a45e73e61e..ac37bb566e 100644 --- a/src/liblink/list9.c +++ b/src/liblink/list9.c @@ -172,7 +172,7 @@ Dconv(Fmt *fp) if(a->u.argsize == ArgsSizeUnknown) sprint(str, "$%lld", a->offset); else - sprint(str, "$%lld-%lld", a->offset, a->u.argsize); + sprint(str, "$%lld-%d", a->offset, a->u.argsize); break; case TYPE_MEM: diff --git a/src/liblink/obj5.c b/src/liblink/obj5.c index 805d7a59c2..abddbc3c4f 100644 --- a/src/liblink/obj5.c +++ b/src/liblink/obj5.c @@ -185,14 +185,6 @@ linkcase(Prog *casep) } static void -nocache5(Prog *p) -{ - p->optab = 0; - p->from.class = 0; - p->to.class = 0; -} - -static void preprocess(Link *ctxt, LSym *cursym) { Prog *p, *pl, *p1, *p2, *q, *q1, *q2; @@ -478,7 +470,7 @@ preprocess(Link *ctxt, LSym *cursym) break; case ARET: - nocache5(p); + nocache(p); if(cursym->text->mark & LEAF) { if(!autosize) { p->as = AB; diff --git a/src/liblink/obj6.c b/src/liblink/obj6.c index cddc723bf2..546c89d53c 100644 --- a/src/liblink/obj6.c +++ b/src/liblink/obj6.c @@ -35,18 +35,6 @@ #include "../cmd/6l/6.out.h" #include "../runtime/stack.h" -static void -nopout(Prog *p) -{ - p->as = ANOP; - p->from.type = TYPE_NONE; - p->from.reg = 0; - p->from.name = 0; - p->to.type = TYPE_NONE; - p->to.reg = 0; - p->to.name = 0; -} - static void nacladdr(Link*, Prog*, Addr*); static int diff --git a/src/liblink/sym.c b/src/liblink/sym.c index 079f600aa9..cae7e4aafe 100644 --- a/src/liblink/sym.c +++ b/src/liblink/sym.c @@ -148,15 +148,15 @@ linknew(LinkArch *arch) switch(ctxt->arch->thechar) { default: sysfatal("unknown thread-local storage offset for nacl/%s", ctxt->arch->name); + case '5': + ctxt->tlsoffset = 0; + break; case '6': ctxt->tlsoffset = 0; break; case '8': ctxt->tlsoffset = -8; break; - case '5': - ctxt->tlsoffset = 0; - break; } break; @@ -174,6 +174,9 @@ linknew(LinkArch *arch) case '8': ctxt->tlsoffset = 0x468; break; + case '5': + ctxt->tlsoffset = 0; // dummy value, not needed + break; } break; } diff --git a/src/math/all_test.go b/src/math/all_test.go index 763efb2e64..0848c506e4 100644 --- a/src/math/all_test.go +++ b/src/math/all_test.go @@ -2606,7 +2606,7 @@ func TestLargeTan(t *testing.T) { // Check that math constants are accepted by compiler // and have right value (assumes strconv.ParseFloat works). -// http://code.google.com/p/go/issues/detail?id=201 +// http://golang.org/issue/201 type floatTest struct { val interface{} diff --git a/src/math/big/float.go b/src/math/big/float.go index ea42a9166e..06b3ae2f40 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -9,7 +9,7 @@ // rounding mode of the result operand determines the rounding // mode of an operation. This is a from-scratch implementation. -// CAUTION: WORK IN PROGRESS - ANY ASPECT OF THIS IMPLEMENTATION MAY CHANGE! +// CAUTION: WORK IN PROGRESS - USE AT YOUR OWN RISK. package big @@ -18,47 +18,38 @@ import ( "math" ) -// TODO(gri): Determine if there's a more natural way to set the precision. -// Should there be a special meaning for prec 0? Such as "full precision"? -// (would be possible for all ops except quotient). - const debugFloat = true // enable for debugging -// Internal representation: A floating-point value x != 0 consists -// of a sign (x.neg), mantissa (x.mant), and exponent (x.exp) such -// that -// -// x = sign * 0.mantissa * 2**exponent +// A Float represents a multi-precision floating point number of the form // -// and the mantissa is interpreted as a value between 0.5 and 1: +// sign × mantissa × 2**exponent // -// 0.5 <= mantissa < 1.0 -// -// The mantissa bits are stored in the shortest nat slice long enough -// to hold x.prec mantissa bits. The mantissa is normalized such that -// the msb of x.mant == 1. Thus, if the precision is not a multiple of -// the Word size _W, x.mant[0] contains trailing zero bits. The number -// 0 is represented by an empty mantissa and a zero exponent. - -// A Float represents a multi-precision floating point number -// of the form +// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp (with the +// exception of 0 and Inf which have a 0 mantissa and special exponents). // -// sign * mantissa * 2**exponent +// Each Float value also has a precision, rounding mode, and accuracy. // -// Each value also has a precision, rounding mode, and accuracy value: -// The precision is the number of mantissa bits used to represent a -// value, and the result of operations is rounded to that many bits -// according to the value's rounding mode (unless specified othewise). -// The accuracy value indicates the rounding error with respect to the -// exact (not rounded) value. +// The precision is the number of mantissa bits used to represent the +// value. The rounding mode specifies how a result should be rounded +// to fit into the mantissa bits, and accuracy describes the rounding +// error with respect to the exact result. // -// The zero value for a Float represents the number 0. +// All operations, including setters, that specify a *Float for the result, +// usually via the receiver, round their result to the result's precision +// and according to its rounding mode, unless specified otherwise. If the +// result precision is 0 (see below), it is set to the precision of the +// argument with the largest precision value before any rounding takes +// place. +// TODO(gri) should the rounding mode also be copied in this case? // -// By setting the desired precision to 24 (or 53) and using ToNearestEven -// rounding, Float arithmetic operations emulate the corresponding float32 -// or float64 IEEE-754 operations (except for denormalized numbers and NaNs). +// By setting the desired precision to 24 or 53 and using ToNearestEven +// rounding, Float operations produce the same results as the corresponding +// float32 or float64 IEEE-754 arithmetic for normalized operands (no NaNs +// or denormalized numbers). Additionally, positive and negative zeros and +// infinities are fully supported. // -// CAUTION: THIS IS WORK IN PROGRESS - USE AT YOUR OWN RISK. +// The zero (uninitialized) value for a Float is ready to use and +// represents the number +0.0 of 0 bit precision. // type Float struct { mode RoundingMode @@ -69,31 +60,42 @@ type Float struct { prec uint // TODO(gri) make this a 32bit field } +// Internal representation details: The mantissa bits x.mant of a Float x +// are stored in the shortest nat slice long enough to hold x.prec bits. +// Unless x is a zero or an infinity, x.mant is normalized such that the +// msb of x.mant == 1. Thus, if the precision is not a multiple of the +// the Word size _W, x.mant[0] contains trailing zero bits. Zero and Inf +// values have an empty mantissa and a 0 or infExp exponent, respectively. + // NewFloat returns a new Float with value x rounded // to prec bits according to the given rounding mode. +// If prec == 0, the result has value 0.0 independent +// of the value of x. +// BUG(gri) For prec == 0 and x == Inf, the result +// should be Inf as well. +// TODO(gri) rethink this signature. func NewFloat(x float64, prec uint, mode RoundingMode) *Float { - // TODO(gri) should make this more efficient - z := new(Float).SetFloat64(x) - return z.Round(z, prec, mode) + var z Float + if prec > 0 { + // TODO(gri) should make this more efficient + z.SetFloat64(x) + return z.Round(&z, prec, mode) + } + z.mode = mode // TODO(gri) don't do this twice for prec > 0 + return &z } -// infExp is the exponent value for infinity. -const infExp = 1<<31 - 1 +const ( + MaxExp = math.MaxInt32 // largest supported exponent magnitude + infExp = -MaxExp - 1 // exponent for Inf values +) -// NewInf returns a new Float with value positive infinity (sign >= 0), -// or negative infinity (sign < 0). +// NewInf returns a new infinite Float value with value +Inf (sign >= 0), +// or -Inf (sign < 0). func NewInf(sign int) *Float { return &Float{neg: sign < 0, exp: infExp} } -func (z *Float) setExp(e int64) { - e32 := int32(e) - if int64(e32) != e { - panic("exponent overflow") // TODO(gri) handle this gracefully - } - z.exp = e32 -} - // Accuracy describes the rounding error produced by the most recent // operation that generated a Float value, relative to the exact value: // @@ -155,7 +157,7 @@ func (mode RoundingMode) String() string { } // Precision returns the mantissa precision of x in bits. -// The precision may be 0 if x == 0. // TODO(gri) Determine a better approach. +// The precision may be 0 for |x| == 0 or |x| == Inf. func (x *Float) Precision() uint { return uint(x.prec) } @@ -170,11 +172,111 @@ func (x *Float) Mode() RoundingMode { return x.mode } +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 or x == -0 +// +1 if x > 0 +// +func (x *Float) Sign() int { + s := 0 + if len(x.mant) != 0 || x.exp == infExp { + s = 1 // non-zero x + } + if x.neg { + s = -s + } + return s +} + +// MantExp breaks x into its mantissa and exponent components. +// It returns mant and exp satisfying x == mant × 2**exp, with +// the absolute value of mant satisfying 0.5 <= |mant| < 1.0. +// mant has the same precision and rounding mode as x. +// +// Special cases are: +// +// ( ±0).MantExp() = ±0, 0 +// (±Inf).MantExp() = ±Inf, 0 +// +// MantExp does not modify x; the result mant is a new Float. +func (x *Float) MantExp() (mant *Float, exp int) { + mant = new(Float).Copy(x) + if x.exp != infExp { + mant.exp = 0 + exp = int(x.exp) + } + return +} + +// SetMantExp is the inverse of MantExp. It sets z to mant × 2**exp and +// and returns z. The result z has the same precision and rounding mode +// as mant. +// +// Special cases are: +// +// z.SetMantExp( ±0, exp) = ±0 +// z.SetMantExp(±Inf, exp) = ±Inf +// +// The result is ±Inf if the magnitude of exp is > MaxExp. +func (z *Float) SetMantExp(mant *Float, exp int) *Float { + z.Copy(mant) + if len(z.mant) == 0 || z.exp == infExp { + return z + } + z.setExp(int64(exp)) + return z +} + +// IsInt reports whether x is an integer. +// ±Inf are not considered integers. +func (x *Float) IsInt() bool { + if debugFloat { + x.validate() + } + // pick off easy cases + if x.exp <= 0 { + // |x| < 1 || |x| == Inf + return len(x.mant) == 0 && x.exp != infExp + } + // x.exp > 0 + return x.prec <= uint(x.exp) || x.minPrec() <= uint(x.exp) // not enough bits for fractional mantissa +} + +// IsInf reports whether x is an infinity, according to sign. +// If sign > 0, IsInf reports whether x is positive infinity. +// If sign < 0, IsInf reports whether x is negative infinity. +// If sign == 0, IsInf reports whether x is either infinity. +func (x *Float) IsInf(sign int) bool { + return x.exp == infExp && (sign == 0 || x.neg == (sign < 0)) +} + +// setExp sets the exponent for z. +// If the exponent's magnitude is too large, z becomes ±Inf. +func (z *Float) setExp(e int64) { + if -MaxExp <= e && e <= MaxExp { + if len(z.mant) == 0 { + e = 0 + } + z.exp = int32(e) + return + } + // Inf + z.mant = z.mant[:0] + z.exp = infExp +} + // debugging support func (x *Float) validate() { - // assumes x != 0 const msb = 1 << (_W - 1) m := len(x.mant) + if m == 0 { + // 0.0 or Inf + if x.exp != 0 && x.exp != infExp { + panic(fmt.Sprintf("empty matissa with invalid exponent %d", x.exp)) + } + return + } if x.mant[m-1]&msb == 0 { panic(fmt.Sprintf("msb not set in last word %#x of %s", x.mant[m-1], x.Format('p', 0))) } @@ -185,21 +287,24 @@ func (x *Float) validate() { // round rounds z according to z.mode to z.prec bits and sets z.acc accordingly. // sbit must be 0 or 1 and summarizes any "sticky bit" information one might -// have before calling round. z's mantissa must be normalized, with the msb set. +// have before calling round. z's mantissa must be normalized (with the msb set) +// or empty. func (z *Float) round(sbit uint) { z.acc = Exact - // handle zero + // handle zero and Inf m := uint(len(z.mant)) // mantissa length in words for current precision if m == 0 { - z.exp = 0 + if z.exp != infExp { + z.exp = 0 + } return } + // z.prec > 0 if debugFloat { z.validate() } - // z.prec > 0 bits := m * _W // available mantissa bits if bits == z.prec { @@ -342,8 +447,9 @@ func (z *Float) round(sbit uint) { } // Round sets z to the value of x rounded according to mode to prec bits and returns z. +// TODO(gri) rethink this signature. func (z *Float) Round(x *Float, prec uint, mode RoundingMode) *Float { - z.Set(x) + z.Copy(x) z.prec = prec z.mode = mode z.round(0) @@ -369,24 +475,33 @@ func nlz64(x uint64) uint { panic("unreachable") } -// SetUint64 sets z to x and returns z. -// Precision is set to 64 bits. +// SetUint64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). func (z *Float) SetUint64(x uint64) *Float { + if z.prec == 0 { + z.prec = 64 + } + z.acc = Exact z.neg = false - z.prec = 64 if x == 0 { z.mant = z.mant[:0] z.exp = 0 return z } + // x != 0 s := nlz64(x) z.mant = z.mant.setUint64(x << s) - z.exp = int32(64 - s) + z.exp = int32(64 - s) // always fits + if z.prec < 64 { + z.round(0) + } return z } -// SetInt64 sets z to x and returns z. -// Precision is set to 64 bits. +// SetInt64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 64 (and rounding will have +// no effect). func (z *Float) SetInt64(x int64) *Float { u := x if u < 0 { @@ -397,27 +512,40 @@ func (z *Float) SetInt64(x int64) *Float { return z } -// SetFloat64 sets z to x and returns z. -// Precision is set to 53 bits. -// TODO(gri) test denormals, +/-Inf, disallow NaN. +// SetFloat64 sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to 53 (and rounding will have +// no effect). +// If x is denormalized or NaN, the result is unspecified. +// TODO(gri) should return nil in those cases func (z *Float) SetFloat64(x float64) *Float { - z.prec = 53 - z.neg = math.Signbit(x) // handle -0 correctly (-0 == 0) + if z.prec == 0 { + z.prec = 53 + } + z.acc = Exact + z.neg = math.Signbit(x) // handle -0 correctly + if math.IsInf(x, 0) { + z.mant = z.mant[:0] + z.exp = infExp + return z + } if x == 0 { z.mant = z.mant[:0] z.exp = 0 return z } + // x != 0 fmant, exp := math.Frexp(x) // get normalized mantissa z.mant = z.mant.setUint64(1<<63 | math.Float64bits(fmant)<<11) - z.exp = int32(exp) + z.exp = int32(exp) // always fits + if z.prec < 53 { + z.round(0) + } return z } // fnorm normalizes mantissa m by shifting it to the left -// such that the msb of the most-significant word (msw) -// is 1. It returns the shift amount. -// It assumes that m is not the zero nat. +// such that the msb of the most-significant word (msw) is 1. +// It returns the shift amount. It assumes that len(m) != 0. func fnorm(m nat) uint { if debugFloat && (len(m) == 0 || m[len(m)-1] == 0) { panic("msw of mantissa is 0") @@ -432,38 +560,80 @@ func fnorm(m nat) uint { return s } -// SetInt sets z to x and returns z. -// Precision is set to the number of bits required to represent x accurately. -// TODO(gri) what about precision for x == 0? +// SetInt sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the larger of x.BitLen() +// or 64 (and rounding will have no effect). func (z *Float) SetInt(x *Int) *Float { + // TODO(gri) can be more efficient if z.prec > 0 + // but small compared to the size of x, or if there + // are many trailing 0's. + bits := uint(x.BitLen()) + if z.prec == 0 { + z.prec = umax(bits, 64) + } + z.acc = Exact + z.neg = x.neg if len(x.abs) == 0 { - z.neg = false z.mant = z.mant[:0] z.exp = 0 - // z.prec = ? return z } // x != 0 - z.neg = x.neg z.mant = z.mant.set(x.abs) - e := uint(len(z.mant))*_W - fnorm(z.mant) - z.exp = int32(e) - z.prec = e + fnorm(z.mant) + z.setExp(int64(bits)) + if z.prec < bits { + z.round(0) + } return z } -// SetRat sets z to x rounded to the precision of z and returns z. -func (z *Float) SetRat(x *Rat, prec uint) *Float { - panic("unimplemented") +// SetRat sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the largest of a.BitLen(), +// b.BitLen(), or 64; with x = a/b. +func (z *Float) SetRat(x *Rat) *Float { + // TODO(gri) can be more efficient if x is an integer + var a, b Float + a.SetInt(x.Num()) + b.SetInt(x.Denom()) + if z.prec == 0 { + z.prec = umax(a.prec, b.prec) + } + return z.Quo(&a, &b) } -// Set sets z to x, with the same precision as x, and returns z. +// Set sets z to the (possibly rounded) value of x and returns z. +// If z's precision is 0, it is changed to the precision of x +// before setting z (and rounding will have no effect). +// Rounding is performed according to z's precision and rounding +// mode; and z's accuracy reports the result error relative to the +// exact (not rounded) result. func (z *Float) Set(x *Float) *Float { if z != x { + if z.prec == 0 { + z.prec = x.prec + } + z.acc = Exact + z.neg = x.neg + z.exp = x.exp + z.mant = z.mant.set(x.mant) + if z.prec < x.prec { + z.round(0) + } + } + return z +} + +// Copy sets z to x, with the same precision and rounding mode as x, +// and returns z. +func (z *Float) Copy(x *Float) *Float { + if z != x { + z.acc = Exact z.neg = x.neg z.exp = x.exp z.mant = z.mant.set(x.mant) z.prec = x.prec + z.mode = x.mode } return z } @@ -484,29 +654,119 @@ func high64(x nat) uint64 { return v } -// TODO(gri) FIX THIS (rounding mode, errors, accuracy, etc.) -func (x *Float) Uint64() uint64 { - m := high64(x.mant) - s := x.exp - if s >= 0 { - return m >> (64 - uint(s)) +// minPrec returns the minimum precision required to represent +// x without loss of accuracy. +// TODO(gri) this might be useful to export, perhaps under a better name +func (x *Float) minPrec() uint { + return uint(len(x.mant))*_W - x.mant.trailingZeroBits() +} + +// Uint64 returns the unsigned integer resulting from truncating x +// towards zero. If 0 <= x <= math.MaxUint64, the result is Exact +// if x is an integer and Below otherwise. +// The result is (0, Above) for x < 0, and (math.MaxUint64, Below) +// for x > math.MaxUint64. +func (x *Float) Uint64() (uint64, Accuracy) { + if debugFloat { + x.validate() } - return 0 // imprecise + switch x.ord() { + case -2, -1: + // x < 0 + return 0, Above + case 0: + // x == 0 || x == -0 + return 0, Exact + case 1: + // 0 < x < +Inf + if x.exp <= 0 { + // 0 < x < 1 + return 0, Below + } + // 1 <= x < +Inf + if x.exp <= 64 { + // u = trunc(x) fits into a uint64 + u := high64(x.mant) >> (64 - uint32(x.exp)) + if x.minPrec() <= 64 { + return u, Exact + } + return u, Below // x truncated + } + fallthrough // x too large + case 2: + // x == +Inf + return math.MaxUint64, Below + } + panic("unreachable") } -// TODO(gri) FIX THIS (rounding mode, errors, etc.) -func (x *Float) Int64() int64 { - v := int64(x.Uint64()) - if x.neg { - return -v +// Int64 returns the integer resulting from truncating x towards zero. +// If math.MinInt64 <= x <= math.MaxInt64, the result is Exact if x is +// an integer, and Above (x < 0) or Below (x > 0) otherwise. +// The result is (math.MinInt64, Above) for x < math.MinInt64, and +// (math.MaxInt64, Below) for x > math.MaxInt64. +func (x *Float) Int64() (int64, Accuracy) { + if debugFloat { + x.validate() } - return v + + switch x.ord() { + case -2: + // x == -Inf + return math.MinInt64, Above + case 0: + // x == 0 || x == -0 + return 0, Exact + case -1, 1: + // 0 < |x| < +Inf + acc := Below + if x.neg { + acc = Above + } + if x.exp <= 0 { + // 0 < |x| < 1 + return 0, acc + } + // 1 <= |x| < +Inf + if x.exp <= 63 { + // i = trunc(x) fits into an int64 (excluding math.MinInt64) + i := int64(high64(x.mant) >> (64 - uint32(x.exp))) + if x.neg { + i = -i + } + if x.minPrec() <= 63 { + return i, Exact + } + return i, acc // x truncated + } + if x.neg { + // check for special case x == math.MinInt64 (i.e., x == -(0.5 << 64)) + if x.exp == 64 && x.minPrec() == 1 { + acc = Exact + } + return math.MinInt64, acc + } + fallthrough + case 2: + // x == +Inf + return math.MaxInt64, Below + } + panic("unreachable") } // Float64 returns the closest float64 value of x // by rounding to nearest with 53 bits precision. // TODO(gri) implement/document error scenarios. func (x *Float) Float64() (float64, Accuracy) { + // x == ±Inf + if x.exp == infExp { + var sign int + if x.neg { + sign = -1 + } + return math.Inf(sign), Exact + } + // x == 0 if len(x.mant) == 0 { return 0, Exact } @@ -521,39 +781,66 @@ func (x *Float) Float64() (float64, Accuracy) { return math.Float64frombits(s | e<<52 | m), r.acc } -func (x *Float) Int() *Int { - if len(x.mant) == 0 { - return new(Int) +// Int returns the result of truncating x towards zero; or nil +// if x is an infinity. The result is Exact if x.IsInt(); +// otherwise it is Below for x > 0, and Above for x < 0. +func (x *Float) Int() (res *Int, acc Accuracy) { + if debugFloat { + x.validate() } - panic("unimplemented") -} - -func (x *Float) Rat() *Rat { - panic("unimplemented") -} - -func (x *Float) IsInt() bool { - if len(x.mant) == 0 { - return true + // accuracy for inexact results + acc = Below // truncation + if x.neg { + acc = Above } + // pick off easy cases if x.exp <= 0 { - return false + // |x| < 1 || |x| == Inf + if x.exp == infExp { + return nil, acc // ±Inf + } + if len(x.mant) == 0 { + acc = Exact // ±0 + } + return new(Int), acc // ±0.xxx } - if uint(x.exp) >= x.prec { - return true + // x.exp > 0 + // x.mant[len(x.mant)-1] != 0 + // determine minimum required precision for x + allBits := uint(len(x.mant)) * _W + exp := uint(x.exp) + if x.minPrec() <= exp { + acc = Exact } + // shift mantissa as needed + res = &Int{neg: x.neg} + // TODO(gri) should have a shift that takes positive and negative shift counts + switch { + case exp > allBits: + res.abs = res.abs.shl(x.mant, exp-allBits) + default: + res.abs = res.abs.set(x.mant) + case exp < allBits: + res.abs = res.abs.shr(x.mant, allBits-exp) + } + return +} + +// BUG(gri) Rat is not yet implemented +func (x *Float) Rat() *Rat { panic("unimplemented") } -// Abs sets z to |x| (the absolute value of x) and returns z. -// TODO(gri) should Abs (and Neg) below ignore z's precision and rounding mode? +// Abs sets z to the (possibly rounded) value |x| (the absolute value of x) +// and returns z. func (z *Float) Abs(x *Float) *Float { z.Set(x) z.neg = false return z } -// Neg sets z to x with its sign negated, and returns z. +// Neg sets z to the (possibly rounded) value of x with its sign negated, +// and returns z. func (z *Float) Neg(x *Float) *Float { z.Set(x) z.neg = !z.neg @@ -561,7 +848,7 @@ func (z *Float) Neg(x *Float) *Float { } // z = x + y, ignoring signs of x and y. -// x and y must not be 0. +// x and y must not be 0 or an Inf. func (z *Float) uadd(x, y *Float) { // Note: This implementation requires 2 shifts most of the // time. It is also inefficient if exponents or precisions @@ -603,7 +890,7 @@ func (z *Float) uadd(x, y *Float) { } // z = x - y for x >= y, ignoring signs of x and y. -// x and y must not be zero. +// x and y must not be 0 or an Inf. func (z *Float) usub(x, y *Float) { // This code is symmetric to uadd. // We have not factored the common code out because @@ -643,7 +930,7 @@ func (z *Float) usub(x, y *Float) { } // z = x * y, ignoring signs of x and y. -// x and y must not be zero. +// x and y must not be 0 or an Inf. func (z *Float) umul(x, y *Float) { if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) { panic("umul called with 0 argument") @@ -664,7 +951,7 @@ func (z *Float) umul(x, y *Float) { } // z = x / y, ignoring signs of x and y. -// x and y must not be zero. +// x and y must not be 0 or an Inf. func (z *Float) uquo(x, y *Float) { if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) { panic("uquo called with 0 argument") @@ -708,7 +995,7 @@ func (z *Float) uquo(x, y *Float) { } // ucmp returns -1, 0, or 1, depending on whether x < y, x == y, or x > y, -// while ignoring the signs of x and y. x and y must not be zero. +// while ignoring the signs of x and y. x and y must not be 0 or an Inf. func (x *Float) ucmp(y *Float) int { if debugFloat && (len(x.mant) == 0 || len(y.mant) == 0) { panic("ucmp called with 0 argument") @@ -765,16 +1052,24 @@ func (x *Float) ucmp(y *Float) int { // sign as x even when x is zero. // Add sets z to the rounded sum x+y and returns z. +// If z's precision is 0, it is changed to the larger +// of x's or y's precision before the operation. // Rounding is performed according to z's precision // and rounding mode; and z's accuracy reports the // result error relative to the exact (not rounded) // result. func (z *Float) Add(x, y *Float) *Float { + if z.prec == 0 { + z.prec = umax(x.prec, y.prec) + } + // TODO(gri) what about -0? if len(y.mant) == 0 { + // TODO(gri) handle Inf return z.Round(x, z.prec, z.mode) } if len(x.mant) == 0 { + // TODO(gri) handle Inf return z.Round(y, z.prec, z.mode) } @@ -799,13 +1094,15 @@ func (z *Float) Add(x, y *Float) *Float { } // Sub sets z to the rounded difference x-y and returns z. -// Rounding is performed according to z's precision -// and rounding mode; and z's accuracy reports the -// result error relative to the exact (not rounded) -// result. +// Precision, rounding, and accuracy reporting are as for Add. func (z *Float) Sub(x, y *Float) *Float { + if z.prec == 0 { + z.prec = umax(x.prec, y.prec) + } + // TODO(gri) what about -0? if len(y.mant) == 0 { + // TODO(gri) handle Inf return z.Round(x, z.prec, z.mode) } if len(x.mant) == 0 { @@ -836,11 +1133,14 @@ func (z *Float) Sub(x, y *Float) *Float { } // Mul sets z to the rounded product x*y and returns z. -// Rounding is performed according to z's precision -// and rounding mode; and z's accuracy reports the -// result error relative to the exact (not rounded) -// result. +// Precision, rounding, and accuracy reporting are as for Add. func (z *Float) Mul(x, y *Float) *Float { + if z.prec == 0 { + z.prec = umax(x.prec, y.prec) + } + + // TODO(gri) handle Inf + // TODO(gri) what about -0? if len(x.mant) == 0 || len(y.mant) == 0 { z.neg = false @@ -857,47 +1157,61 @@ func (z *Float) Mul(x, y *Float) *Float { } // Quo sets z to the rounded quotient x/y and returns z. -// If y == 0, a division-by-zero run-time panic occurs. TODO(gri) this should become Inf -// Rounding is performed according to z's precision -// and rounding mode; and z's accuracy reports the -// result error relative to the exact (not rounded) -// result. +// Precision, rounding, and accuracy reporting are as for Add. func (z *Float) Quo(x, y *Float) *Float { - // TODO(gri) what about -0? + if z.prec == 0 { + z.prec = umax(x.prec, y.prec) + } + + // TODO(gri) handle Inf + + // TODO(gri) check that this is correct + z.neg = x.neg != y.neg + + if len(y.mant) == 0 { + z.setExp(infExp) + return z + } + if len(x.mant) == 0 { - z.neg = false z.mant = z.mant[:0] z.exp = 0 z.acc = Exact return z } - if len(y.mant) == 0 { - panic("division-by-zero") // TODO(gri) handle this better - } // x, y != 0 z.uquo(x, y) - z.neg = x.neg != y.neg return z } // Lsh sets z to the rounded x * (1<<s) and returns z. +// If z's precision is 0, it is changed to x's precision. // Rounding is performed according to z's precision // and rounding mode; and z's accuracy reports the // result error relative to the exact (not rounded) // result. func (z *Float) Lsh(x *Float, s uint, mode RoundingMode) *Float { + if z.prec == 0 { + z.prec = x.prec + } + + // TODO(gri) handle Inf + z.Round(x, z.prec, mode) z.setExp(int64(z.exp) + int64(s)) return z } // Rsh sets z to the rounded x / (1<<s) and returns z. -// Rounding is performed according to z's precision -// and rounding mode; and z's accuracy reports the -// result error relative to the exact (not rounded) -// result. +// Precision, rounding, and accuracy reporting are as for Lsh. func (z *Float) Rsh(x *Float, s uint, mode RoundingMode) *Float { + if z.prec == 0 { + z.prec = x.prec + } + + // TODO(gri) handle Inf + z.Round(x, z.prec, mode) z.setExp(int64(z.exp) - int64(s)) return z @@ -909,49 +1223,60 @@ func (z *Float) Rsh(x *Float, s uint, mode RoundingMode) *Float { // 0 if x == y (incl. -0 == 0) // +1 if x > y // +// Infinities with matching sign are equal. func (x *Float) Cmp(y *Float) int { - // special cases - switch { - case len(x.mant) == 0: - // 0 cmp y == -sign(y) - return -y.Sign() - case len(y.mant) == 0: - // x cmp 0 == sign(x) - return x.Sign() + if debugFloat { + x.validate() + y.validate() } - // x != 0 && y != 0 - // x cmp y == x cmp y - // x cmp (-y) == 1 - // (-x) cmp y == -1 - // (-x) cmp (-y) == -(x cmp y) + mx := x.ord() + my := y.ord() switch { - case x.neg == y.neg: - r := x.ucmp(y) - if x.neg { - r = -r - } - return r - case x.neg: + case mx < my: return -1 - default: - return 1 + case mx > my: + return +1 } + // mx == my + + // only if |mx| == 1 we have to compare the mantissae + switch mx { + case -1: + return -x.ucmp(y) + case +1: + return +x.ucmp(y) + } + return 0 } -// Sign returns: +func umax(x, y uint) uint { + if x > y { + return x + } + return y +} + +// ord classifies x and returns: // -// -1 if x < 0 -// 0 if x == 0 (incl. x == -0) -// +1 if x > 0 +// -2 if -Inf == x +// -1 if -Inf < x < 0 +// 0 if x == 0 (signed or unsigned) +// +1 if 0 < x < +Inf +// +2 if x == +Inf // -func (x *Float) Sign() int { +// TODO(gri) export (and remove IsInf)? +func (x *Float) ord() int { + m := 1 // common case if len(x.mant) == 0 { - return 0 + m = 0 + if x.exp == infExp { + m = 2 + } } if x.neg { - return -1 + m = -m } - return 1 + return m } diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index ec67a6d606..dd059ba4a5 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -6,11 +6,210 @@ package big import ( "fmt" + "math" "sort" "strconv" + "strings" "testing" ) +func (x *Float) uint64() uint64 { + u, acc := x.Uint64() + if acc != Exact { + panic(fmt.Sprintf("%s is not a uint64", x.Format('g', 10))) + } + return u +} + +func (x *Float) int64() int64 { + i, acc := x.Int64() + if acc != Exact { + panic(fmt.Sprintf("%s is not an int64", x.Format('g', 10))) + } + return i +} + +func TestFloatZeroValue(t *testing.T) { + // zero (uninitialized) value is a ready-to-use 0.0 + var x Float + if s := x.Format('f', 1); s != "0.0" { + t.Errorf("zero value = %s; want 0.0", s) + } + + // zero value has precision 0 + if prec := x.Precision(); prec != 0 { + t.Errorf("prec = %d; want 0", prec) + } + + // zero value can be used in any and all positions of binary operations + make := func(x int) *Float { + if x == 0 { + return new(Float) // 0 translates into the zero value + } + return NewFloat(float64(x), 10, 0) + } + for _, test := range []struct { + z, x, y, want int + opname rune + op func(z, x, y *Float) *Float + }{ + {0, 0, 0, 0, '+', (*Float).Add}, + {0, 1, 2, 3, '+', (*Float).Add}, + {1, 2, 0, 2, '+', (*Float).Add}, + {2, 0, 1, 1, '+', (*Float).Add}, + + {0, 0, 0, 0, '-', (*Float).Sub}, + {0, 1, 2, -1, '-', (*Float).Sub}, + {1, 2, 0, 2, '-', (*Float).Sub}, + {2, 0, 1, -1, '-', (*Float).Sub}, + + {0, 0, 0, 0, '*', (*Float).Mul}, + {0, 1, 2, 2, '*', (*Float).Mul}, + {1, 2, 0, 0, '*', (*Float).Mul}, + {2, 0, 1, 0, '*', (*Float).Mul}, + + {0, 0, 0, 0, '/', (*Float).Quo}, // = +Inf + {0, 2, 1, 2, '/', (*Float).Quo}, + {1, 2, 0, 0, '/', (*Float).Quo}, // = +Inf + {2, 0, 1, 0, '/', (*Float).Quo}, + } { + z := make(test.z) + test.op(z, make(test.x), make(test.y)) + got := 0 + if !z.IsInf(0) { + got = int(z.int64()) + } + if got != test.want { + t.Errorf("%d %c %d = %d; want %d", test.x, test.opname, test.y, got, test.want) + } + } + + // TODO(gri) test how precision is set for zero value results +} + +func makeFloat(s string) *Float { + if s == "Inf" || s == "+Inf" { + return NewInf(+1) + } + if s == "-Inf" { + return NewInf(-1) + } + var x Float + x.prec = 1000 // TODO(gri) find a better way to do this + if _, ok := x.SetString(s); !ok { + panic(fmt.Sprintf("%q is not a valid float", s)) + } + return &x +} + +func TestFloatSign(t *testing.T) { + for _, test := range []struct { + x string + s int + }{ + {"-Inf", -1}, + {"-1", -1}, + {"-0", 0}, + {"+0", 0}, + {"+1", +1}, + {"+Inf", +1}, + } { + x := makeFloat(test.x) + s := x.Sign() + if s != test.s { + t.Errorf("%s.Sign() = %d; want %d", test.x, s, test.s) + } + } +} + +// feq(x, y) is like x.Cmp(y) == 0 but it also considers the sign of 0 (0 != -0). +func feq(x, y *Float) bool { + return x.Cmp(y) == 0 && x.neg == y.neg +} + +func TestFloatMantExp(t *testing.T) { + for _, test := range []struct { + x string + frac string + exp int + }{ + {"0", "0", 0}, + {"+0", "0", 0}, + {"-0", "-0", 0}, + {"Inf", "+Inf", 0}, + {"+Inf", "+Inf", 0}, + {"-Inf", "-Inf", 0}, + {"1.5", "0.75", 1}, + {"1.024e3", "0.5", 11}, + {"-0.125", "-0.5", -2}, + } { + x := makeFloat(test.x) + frac := makeFloat(test.frac) + f, e := x.MantExp() + if !feq(f, frac) || e != test.exp { + t.Errorf("%s.MantExp() = %s, %d; want %s, %d", test.x, f.Format('g', 10), e, test.frac, test.exp) + } + } +} + +func TestFloatSetMantExp(t *testing.T) { + for _, test := range []struct { + frac string + exp int + z string + }{ + {"0", 0, "0"}, + {"+0", 0, "0"}, + {"-0", 0, "-0"}, + {"Inf", 1234, "+Inf"}, + {"+Inf", -1234, "+Inf"}, + {"-Inf", -1234, "-Inf"}, + {"0", -MaxExp - 1, "0"}, + {"1", -MaxExp - 1, "+Inf"}, // exponent magnitude too large + {"-1", -MaxExp - 1, "-Inf"}, // exponent magnitude too large + {"0.75", 1, "1.5"}, + {"0.5", 11, "1024"}, + {"-0.5", -2, "-0.125"}, + } { + frac := makeFloat(test.frac) + want := makeFloat(test.z) + var z Float + z.SetMantExp(frac, test.exp) + if !feq(&z, want) { + t.Errorf("SetMantExp(%s, %d) = %s; want %s", test.frac, test.exp, z.Format('g', 10), test.z) + } + } +} + +func TestFloatIsInt(t *testing.T) { + for _, test := range []string{ + "0 int", + "-0 int", + "1 int", + "-1 int", + "0.5", + "1.23", + "1.23e1", + "1.23e2 int", + "0.000000001e+8", + "0.000000001e+9 int", + "1.2345e200 int", + "Inf", + "+Inf", + "-Inf", + } { + s := strings.TrimSuffix(test, " int") + want := s != test + if got := makeFloat(s).IsInt(); got != want { + t.Errorf("%s.IsInt() == %t", s, got) + } + } +} + +func TestFloatIsInf(t *testing.T) { + // TODO(gri) implement this +} + func fromBinary(s string) int64 { x, err := strconv.ParseInt(s, 2, 64) if err != nil { @@ -66,7 +265,7 @@ func testFloatRound(t *testing.T, x, r int64, prec uint, mode RoundingMode) { f.Round(f, prec, mode) // check result - r1 := f.Int64() + r1 := f.int64() p1 := f.Precision() a1 := f.Accuracy() if r1 != r || p1 != prec || a1 != a { @@ -203,9 +402,21 @@ func TestFloatSetUint64(t *testing.T) { 1 << 32, 1<<64 - 1, } { - f := new(Float).SetUint64(want) - if got := f.Uint64(); got != want { - t.Errorf("got %d (%s); want %d", got, f.Format('p', 0), want) + var f Float + f.SetUint64(want) + if got := f.uint64(); got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x uint64 = 0x8765432187654321 // 64 bits needed + for prec := uint(1); prec <= 64; prec++ { + f := NewFloat(0, prec, ToZero).SetUint64(x) + got := f.uint64() + want := x &^ (1<<(64-prec) - 1) // cut off (round to zero) low 64-prec bits + if got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) } } } @@ -225,12 +436,24 @@ func TestFloatSetInt64(t *testing.T) { if i&1 != 0 { want = -want } - f := new(Float).SetInt64(want) - if got := f.Int64(); got != want { - t.Errorf("got %d (%s); want %d", got, f.Format('p', 0), want) + var f Float + f.SetInt64(want) + if got := f.int64(); got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) } } } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x int64 = 0x7654321076543210 // 63 bits needed + for prec := uint(1); prec <= 63; prec++ { + f := NewFloat(0, prec, ToZero).SetInt64(x) + got := f.int64() + want := x &^ (1<<(63-prec) - 1) // cut off (round to zero) low 63-prec bits + if got != want { + t.Errorf("got %#x (%s); want %#x", got, f.Format('p', 0), want) + } + } } func TestFloatSetFloat64(t *testing.T) { @@ -244,21 +467,265 @@ func TestFloatSetFloat64(t *testing.T) { 3.14159265e10, 2.718281828e-123, 1.0 / 3, + math.Inf(-1), + math.Inf(0), + -math.Inf(1), } { for i := range [2]int{} { if i&1 != 0 { want = -want } - f := new(Float).SetFloat64(want) + var f Float + f.SetFloat64(want) if got, _ := f.Float64(); got != want { t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want) } } } + + // test basic rounding behavior (exhaustive rounding testing is done elsewhere) + const x uint64 = 0x8765432143218 // 53 bits needed + for prec := uint(1); prec <= 52; prec++ { + f := NewFloat(0, prec, ToZero).SetFloat64(float64(x)) + got, _ := f.Float64() + want := float64(x &^ (1<<(52-prec) - 1)) // cut off (round to zero) low 53-prec bits + if got != want { + t.Errorf("got %g (%s); want %g", got, f.Format('p', 0), want) + } + } } func TestFloatSetInt(t *testing.T) { - // TODO(gri) implement + for _, want := range []string{ + "0", + "1", + "-1", + "1234567890", + "123456789012345678901234567890", + "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + } { + var x Int + _, ok := x.SetString(want, 0) + if !ok { + t.Errorf("invalid integer %s", want) + continue + } + n := x.BitLen() + + var f Float + f.SetInt(&x) + + // check precision + if n < 64 { + n = 64 + } + if prec := f.Precision(); prec != uint(n) { + t.Errorf("got prec = %d; want %d", prec, n) + } + + // check value + got := f.Format('g', 100) + if got != want { + t.Errorf("got %s (%s); want %s", got, f.Format('p', 0), want) + } + } + + // TODO(gri) test basic rounding behavior +} + +func TestFloatSetRat(t *testing.T) { + for _, want := range []string{ + "0", + "1", + "-1", + "1234567890", + "123456789012345678901234567890", + "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "1.2", + "3.14159265", + // TODO(gri) expand + } { + var x Rat + _, ok := x.SetString(want) + if !ok { + t.Errorf("invalid fraction %s", want) + continue + } + n := max(x.Num().BitLen(), x.Denom().BitLen()) + + var f1 Float + var f2 = NewFloat(0, 1000, 0) // set a high precision - TODO(gri) find a cleaner way + f1.SetRat(&x) + f2.SetRat(&x) + + // check precision when set automatically + if n < 64 { + n = 64 + } + if prec := f1.Precision(); prec != uint(n) { + t.Errorf("got prec = %d; want %d", prec, n) + } + + got := f2.Format('g', 100) + if got != want { + t.Errorf("got %s (%s); want %s", got, f2.Format('p', 0), want) + } + } +} + +func TestFloatUint64(t *testing.T) { + for _, test := range []struct { + x string + out uint64 + acc Accuracy + }{ + {"-Inf", 0, Above}, + {"-1", 0, Above}, + {"-1e-1000", 0, Above}, + {"-0", 0, Exact}, + {"0", 0, Exact}, + {"1e-1000", 0, Below}, + {"1", 1, Exact}, + {"1.000000000000000000001", 1, Below}, + {"12345.0", 12345, Exact}, + {"12345.000000000000000000001", 12345, Below}, + {"18446744073709551615", 18446744073709551615, Exact}, + {"18446744073709551615.000000000000000000001", math.MaxUint64, Below}, + {"18446744073709551616", math.MaxUint64, Below}, + {"1e10000", math.MaxUint64, Below}, + {"+Inf", math.MaxUint64, Below}, + } { + x := makeFloat(test.x) + out, acc := x.Uint64() + if out != test.out || acc != test.acc { + t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) + } + } +} + +func TestFloatInt64(t *testing.T) { + for _, test := range []struct { + x string + out int64 + acc Accuracy + }{ + {"-Inf", math.MinInt64, Above}, + {"-1e10000", math.MinInt64, Above}, + {"-9223372036854775809", math.MinInt64, Above}, + {"-9223372036854775808.000000000000000000001", math.MinInt64, Above}, + {"-9223372036854775808", -9223372036854775808, Exact}, + {"-9223372036854775807.000000000000000000001", -9223372036854775807, Above}, + {"-9223372036854775807", -9223372036854775807, Exact}, + {"-12345.000000000000000000001", -12345, Above}, + {"-12345.0", -12345, Exact}, + {"-1.000000000000000000001", -1, Above}, + {"-1", -1, Exact}, + {"-1e-1000", 0, Above}, + {"0", 0, Exact}, + {"1e-1000", 0, Below}, + {"1", 1, Exact}, + {"1.000000000000000000001", 1, Below}, + {"12345.0", 12345, Exact}, + {"12345.000000000000000000001", 12345, Below}, + {"9223372036854775807", 9223372036854775807, Exact}, + {"9223372036854775807.000000000000000000001", math.MaxInt64, Below}, + {"9223372036854775808", math.MaxInt64, Below}, + {"1e10000", math.MaxInt64, Below}, + {"+Inf", math.MaxInt64, Below}, + } { + x := makeFloat(test.x) + out, acc := x.Int64() + if out != test.out || acc != test.acc { + t.Errorf("%s: got %d (%s); want %d (%s)", test.x, out, acc, test.out, test.acc) + } + } +} + +func TestFloatInt(t *testing.T) { + for _, test := range []struct { + x string + out string + acc Accuracy + }{ + {"0", "0", Exact}, + {"+0", "0", Exact}, + {"-0", "0", Exact}, + {"Inf", "nil", Below}, + {"+Inf", "nil", Below}, + {"-Inf", "nil", Above}, + {"1", "1", Exact}, + {"-1", "-1", Exact}, + {"1.23", "1", Below}, + {"-1.23", "-1", Above}, + {"123e-2", "1", Below}, + {"123e-3", "0", Below}, + {"123e-4", "0", Below}, + {"1e-1000", "0", Below}, + {"-1e-1000", "0", Above}, + {"1e+10", "10000000000", Exact}, + {"1e+100", "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", Exact}, + } { + x := makeFloat(test.x) + out, acc := x.Int() + got := "nil" + if out != nil { + got = out.String() + } + if got != test.out || acc != test.acc { + t.Errorf("%s: got %s (%s); want %s (%s)", test.x, got, acc, test.out, test.acc) + } + } +} + +func TestFloatRat(t *testing.T) { + // TODO(gri) implement this +} + +func TestFloatAbs(t *testing.T) { + for _, test := range []string{ + "0", + "1", + "1234", + "1.23e-2", + "1e-1000", + "1e1000", + "Inf", + } { + p := makeFloat(test) + a := new(Float).Abs(p) + if !feq(a, p) { + t.Errorf("%s: got %s; want %s", test, a.Format('g', 10), test) + } + + n := makeFloat("-" + test) + a.Abs(n) + if !feq(a, p) { + t.Errorf("-%s: got %s; want %s", test, a.Format('g', 10), test) + } + } +} + +func TestFloatNeg(t *testing.T) { + for _, test := range []string{ + "0", + "1", + "1234", + "1.23e-2", + "1e-1000", + "1e1000", + "Inf", + } { + p1 := makeFloat(test) + n1 := makeFloat("-" + test) + n2 := new(Float).Neg(p1) + p2 := new(Float).Neg(n2) + if !feq(n2, n1) { + t.Errorf("%s: got %s; want %s", test, n2.Format('g', 10), n1.Format('g', 10)) + } + if !feq(p2, p1) { + t.Errorf("%s: got %s; want %s", test, p2.Format('g', 10), p1.Format('g', 10)) + } + } } // Selected precisions with which to run various tests. @@ -386,6 +853,7 @@ func TestFloatAdd64(t *testing.T) { } func TestFloatMul(t *testing.T) { + // TODO(gri) implement this } // TestFloatMul64 tests that Float.Mul/Quo of numbers with @@ -521,6 +989,51 @@ func TestFloatQuo(t *testing.T) { } } +// TestFloatQuoSmoke tests all divisions x/y for values x, y in the range [-n, +n]; +// it serves as a smoke test for basic correctness of division. +func TestFloatQuoSmoke(t *testing.T) { + n := 1000 + if testing.Short() { + n = 10 + } + + const dprec = 3 // max. precision variation + const prec = 10 + dprec // enough bits to hold n precisely + for x := -n; x <= n; x++ { + for y := -n; y < n; y++ { + if y == 0 { + continue + } + + a := float64(x) + b := float64(y) + c := a / b + + // vary operand precision (only ok as long as a, b can be represented correctly) + for ad := -dprec; ad <= dprec; ad++ { + for bd := -dprec; bd <= dprec; bd++ { + A := NewFloat(a, uint(prec+ad), 0) + B := NewFloat(b, uint(prec+bd), 0) + C := NewFloat(0, 53, 0).Quo(A, B) // C has float64 mantissa width + + cc, acc := C.Float64() + if cc != c { + t.Errorf("%g/%g = %s; want %.5g\n", a, b, C.Format('g', 5), c) + continue + } + if acc != Exact { + t.Errorf("%g/%g got %s result; want exact result", a, b, acc) + } + } + } + } + } +} + +func TestFloatCmp(t *testing.T) { + // TODO(gri) implement this +} + // normBits returns the normalized bits for x: It // removes multiple equal entries by treating them // as an addition (e.g., []int{5, 5} => []int{6}), diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 06c1f14471..71920cd51c 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -57,6 +57,7 @@ func (z *Float) SetString(s string) (*Float, bool) { // with base 0 or 10 corresponds to the value 1.2 * 2**3. // // BUG(gri) This signature conflicts with Scan(s fmt.ScanState, ch rune) error. +// TODO(gri) What should the default precision be? func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) { // sign z.neg, err = scanSign(r) @@ -162,8 +163,8 @@ func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b i // Format converts the floating-point number x to a string according // to the given format and precision prec. The format is one of: // -// 'e' -d.dddde±dd, decimal exponent -// 'E' -d.ddddE±dd, decimal exponent +// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits +// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits // 'f' -ddddd.dddd, no exponent // 'g' like 'e' for large exponents, like 'f' otherwise // 'G' like 'E' for large exponents, like 'f' otherwise @@ -182,7 +183,7 @@ func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b i // number of digits necessary such that ParseFloat will return f exactly. // The prec value is ignored for the 'b' or 'p' format. // -// BUG(gri) Currently, Format only accepts the 'b' and 'p' format. +// BUG(gri) Currently, Format does not accept negative precisions. func (x *Float) Format(format byte, prec int) string { const extra = 10 // TODO(gri) determine a good/better value here return string(x.Append(make([]byte, 0, prec+extra), format, prec)) @@ -191,13 +192,27 @@ func (x *Float) Format(format byte, prec int) string { // Append appends the string form of the floating-point number x, // as generated by x.Format, to buf and returns the extended buffer. func (x *Float) Append(buf []byte, format byte, prec int) []byte { + // TODO(gri) factor out handling of sign? + + // Inf + if x.IsInf(0) { + var ch byte = '+' + if x.neg { + ch = '-' + } + buf = append(buf, ch) + return append(buf, "Inf"...) + } + + // easy formats switch format { case 'b': return x.bstring(buf) case 'p': return x.pstring(buf) } - return append(buf, fmt.Sprintf(`%%!c`, format)...) + + return x.bigFtoa(buf, format, prec) } // BUG(gri): Currently, String uses the 'p' (rather than 'g') format. @@ -211,7 +226,6 @@ func (x *Float) String() string { // The mantissa is normalized such that is uses x.Precision() bits in binary // representation. func (x *Float) bstring(buf []byte) []byte { - // TODO(gri) handle Inf if x.neg { buf = append(buf, '-') } @@ -239,7 +253,6 @@ func (x *Float) bstring(buf []byte) []byte { // ad returns the extended buffer. // The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0. func (x *Float) pstring(buf []byte) []byte { - // TODO(gri) handle Inf if x.neg { buf = append(buf, '-') } diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index 0e8bfb39ab..11e5df448a 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -5,6 +5,8 @@ package big import ( + "io" + "math" "strconv" "testing" ) @@ -71,37 +73,157 @@ func TestFloatSetFloat64String(t *testing.T) { } } -func TestFloatFormat(t *testing.T) { +const ( + below1e23 = 99999999999999974834176 + above1e23 = 100000000000000008388608 +) + +func TestFloat64Format(t *testing.T) { for _, test := range []struct { - x string + x float64 format byte prec int want string }{ - {"0", 'b', 0, "0"}, - {"-0", 'b', 0, "-0"}, - {"1.0", 'b', 0, "4503599627370496p-52"}, - {"-1.0", 'b', 0, "-4503599627370496p-52"}, - {"4503599627370496", 'b', 0, "4503599627370496p+0"}, + {0, 'f', 0, "0"}, + {math.Copysign(0, -1), 'f', 0, "-0"}, + {1, 'f', 0, "1"}, + {-1, 'f', 0, "-1"}, - {"0", 'p', 0, "0"}, - {"-0", 'p', 0, "-0"}, - {"1024.0", 'p', 0, "0x.8p11"}, - {"-1024.0", 'p', 0, "-0x.8p11"}, - } { - f64, err := strconv.ParseFloat(test.x, 64) - if err != nil { - t.Error(err) - continue - } + {1.459, 'e', 0, "1e+00"}, + {2.459, 'e', 1, "2.5e+00"}, + {3.459, 'e', 2, "3.46e+00"}, + {4.459, 'e', 3, "4.459e+00"}, + {5.459, 'e', 4, "5.4590e+00"}, + + {1.459, 'f', 0, "1"}, + {2.459, 'f', 1, "2.5"}, + {3.459, 'f', 2, "3.46"}, + {4.459, 'f', 3, "4.459"}, + {5.459, 'f', 4, "5.4590"}, + + {0, 'b', 0, "0"}, + {math.Copysign(0, -1), 'b', 0, "-0"}, + {1.0, 'b', 0, "4503599627370496p-52"}, + {-1.0, 'b', 0, "-4503599627370496p-52"}, + {4503599627370496, 'b', 0, "4503599627370496p+0"}, + + {0, 'p', 0, "0"}, + {math.Copysign(0, -1), 'p', 0, "-0"}, + {1024.0, 'p', 0, "0x.8p11"}, + {-1024.0, 'p', 0, "-0x.8p11"}, + + // all test cases below from strconv/ftoa_test.go + {1, 'e', 5, "1.00000e+00"}, + {1, 'f', 5, "1.00000"}, + {1, 'g', 5, "1"}, + // {1, 'g', -1, "1"}, + // {20, 'g', -1, "20"}, + // {1234567.8, 'g', -1, "1.2345678e+06"}, + // {200000, 'g', -1, "200000"}, + // {2000000, 'g', -1, "2e+06"}, + + // g conversion and zero suppression + {400, 'g', 2, "4e+02"}, + {40, 'g', 2, "40"}, + {4, 'g', 2, "4"}, + {.4, 'g', 2, "0.4"}, + {.04, 'g', 2, "0.04"}, + {.004, 'g', 2, "0.004"}, + {.0004, 'g', 2, "0.0004"}, + {.00004, 'g', 2, "4e-05"}, + {.000004, 'g', 2, "4e-06"}, + + {0, 'e', 5, "0.00000e+00"}, + {0, 'f', 5, "0.00000"}, + {0, 'g', 5, "0"}, + // {0, 'g', -1, "0"}, + + {-1, 'e', 5, "-1.00000e+00"}, + {-1, 'f', 5, "-1.00000"}, + {-1, 'g', 5, "-1"}, + // {-1, 'g', -1, "-1"}, - f := new(Float).SetFloat64(f64) + {12, 'e', 5, "1.20000e+01"}, + {12, 'f', 5, "12.00000"}, + {12, 'g', 5, "12"}, + // {12, 'g', -1, "12"}, + + {123456700, 'e', 5, "1.23457e+08"}, + {123456700, 'f', 5, "123456700.00000"}, + {123456700, 'g', 5, "1.2346e+08"}, + // {123456700, 'g', -1, "1.234567e+08"}, + + {1.2345e6, 'e', 5, "1.23450e+06"}, + {1.2345e6, 'f', 5, "1234500.00000"}, + {1.2345e6, 'g', 5, "1.2345e+06"}, + + {1e23, 'e', 17, "9.99999999999999916e+22"}, + {1e23, 'f', 17, "99999999999999991611392.00000000000000000"}, + {1e23, 'g', 17, "9.9999999999999992e+22"}, + + // {1e23, 'e', -1, "1e+23"}, + // {1e23, 'f', -1, "100000000000000000000000"}, + // {1e23, 'g', -1, "1e+23"}, + + {below1e23, 'e', 17, "9.99999999999999748e+22"}, + {below1e23, 'f', 17, "99999999999999974834176.00000000000000000"}, + {below1e23, 'g', 17, "9.9999999999999975e+22"}, + + // {below1e23, 'e', -1, "9.999999999999997e+22"}, + // {below1e23, 'f', -1, "99999999999999970000000"}, + // {below1e23, 'g', -1, "9.999999999999997e+22"}, + + {above1e23, 'e', 17, "1.00000000000000008e+23"}, + {above1e23, 'f', 17, "100000000000000008388608.00000000000000000"}, + // {above1e23, 'g', 17, "1.0000000000000001e+23"}, + + // {above1e23, 'e', -1, "1.0000000000000001e+23"}, + // {above1e23, 'f', -1, "100000000000000010000000"}, + // {above1e23, 'g', -1, "1.0000000000000001e+23"}, + + // {fdiv(5e-304, 1e20), 'g', -1, "5e-324"}, + // {fdiv(-5e-304, 1e20), 'g', -1, "-5e-324"}, + + // {32, 'g', -1, "32"}, + // {32, 'g', 0, "3e+01"}, + + // {100, 'x', -1, "%x"}, + + // {math.NaN(), 'g', -1, "NaN"}, + // {-math.NaN(), 'g', -1, "NaN"}, + {math.Inf(0), 'g', -1, "+Inf"}, + {math.Inf(-1), 'g', -1, "-Inf"}, + {-math.Inf(0), 'g', -1, "-Inf"}, + + {-1, 'b', -1, "-4503599627370496p-52"}, + + // fixed bugs + {0.9, 'f', 1, "0.9"}, + {0.09, 'f', 1, "0.1"}, + {0.0999, 'f', 1, "0.1"}, + {0.05, 'f', 1, "0.1"}, + {0.05, 'f', 0, "0"}, + {0.5, 'f', 1, "0.5"}, + {0.5, 'f', 0, "0"}, + {1.5, 'f', 0, "2"}, + + // http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/ + // {2.2250738585072012e-308, 'g', -1, "2.2250738585072014e-308"}, + // http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ + // {2.2250738585072011e-308, 'g', -1, "2.225073858507201e-308"}, + + // Issue 2625. + {383260575764816448, 'f', 0, "383260575764816448"}, + // {383260575764816448, 'g', -1, "3.8326057576481645e+17"}, + } { + f := new(Float).SetFloat64(test.x) got := f.Format(test.format, test.prec) if got != test.want { t.Errorf("%v: got %s; want %s", test, got, test.want) } - if test.format == 'b' && f64 == 0 { + if test.format == 'b' && test.x == 0 { continue // 'b' format in strconv.Float requires knowledge of bias for 0.0 } if test.format == 'p' { @@ -109,9 +231,97 @@ func TestFloatFormat(t *testing.T) { } // verify that Float format matches strconv format - want := strconv.FormatFloat(f64, test.format, test.prec, 64) + want := strconv.FormatFloat(test.x, test.format, test.prec, 64) if got != want { - t.Errorf("%v: got %s; want %s", test, got, want) + t.Errorf("%v: got %s; want %s (strconv)", test, got, want) + } + } +} + +func TestFloatFormat(t *testing.T) { + for _, test := range []struct { + x string + format byte + prec int + want string + }{ + {"0", 'f', 0, "0"}, + {"-0", 'f', 0, "-0"}, + {"1", 'f', 0, "1"}, + {"-1", 'f', 0, "-1"}, + + {"1.459", 'e', 0, "1e+00"}, + {"2.459", 'e', 1, "2.5e+00"}, + {"3.459", 'e', 2, "3.46e+00"}, + {"4.459", 'e', 3, "4.459e+00"}, + {"5.459", 'e', 4, "5.4590e+00"}, + + {"1.459", 'E', 0, "1E+00"}, + {"2.459", 'E', 1, "2.5E+00"}, + {"3.459", 'E', 2, "3.46E+00"}, + {"4.459", 'E', 3, "4.459E+00"}, + {"5.459", 'E', 4, "5.4590E+00"}, + + {"1.459", 'f', 0, "1"}, + {"2.459", 'f', 1, "2.5"}, + {"3.459", 'f', 2, "3.46"}, + {"4.459", 'f', 3, "4.459"}, + {"5.459", 'f', 4, "5.4590"}, + + {"1.459", 'g', 0, "1"}, + {"2.459", 'g', 1, "2"}, + {"3.459", 'g', 2, "3.5"}, + {"4.459", 'g', 3, "4.46"}, + {"5.459", 'g', 4, "5.459"}, + + {"1459", 'g', 0, "1e+03"}, + {"2459", 'g', 1, "2e+03"}, + {"3459", 'g', 2, "3.5e+03"}, + {"4459", 'g', 3, "4.46e+03"}, + {"5459", 'g', 4, "5459"}, + + {"1459", 'G', 0, "1E+03"}, + {"2459", 'G', 1, "2E+03"}, + {"3459", 'G', 2, "3.5E+03"}, + {"4459", 'G', 3, "4.46E+03"}, + {"5459", 'G', 4, "5459"}, + + {"3", 'e', 40, "3.0000000000000000000000000000000000000000e+00"}, + {"3", 'f', 40, "3.0000000000000000000000000000000000000000"}, + {"3", 'g', 40, "3"}, + + {"3e40", 'e', 40, "3.0000000000000000000000000000000000000000e+40"}, + {"3e40", 'f', 4, "30000000000000000000000000000000000000000.0000"}, + {"3e40", 'g', 40, "3e+40"}, + + // TODO(gri) need tests for actual large Floats + + // These depend on the selected mantissa length to match strconv.FormatFloat. + // Disabled for now. + // {"0", 'b', 0, "0"}, + // {"-0", 'b', 0, "-0"}, + // {"1.0", 'b', 0, "4503599627370496p-52"}, + // {"-1.0", 'b', 0, "-4503599627370496p-52"}, + // {"4503599627370496", 'b', 0, "4503599627370496p+0"}, + + {"0", 'p', 0, "0"}, + {"-0", 'p', 0, "-0"}, + {"1024.0", 'p', 0, "0x.8p11"}, + {"-1024.0", 'p', 0, "-0x.8p11"}, + + // unsupported format + {"3.14", 'x', 0, "%x"}, + } { + f, _, err := ParseFloat(test.x, 0, 1000, ToNearestEven) + // TODO(gri) should we return io.EOF at the end? + if err != nil && err != io.EOF { + t.Errorf("%v: %s", test, err) + continue + } + + got := f.Format(test.format, test.prec) + if got != test.want { + t.Errorf("%v: got %s; want %s", test, got, test.want) } } } diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go new file mode 100644 index 0000000000..148081589d --- /dev/null +++ b/src/math/big/ftoa.go @@ -0,0 +1,184 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements the 'e', 'f', 'g' floating-point formats. +// It is closely following the corresponding implementation in +// strconv/ftoa.go, but modified and simplified for big.Float. + +// Algorithm: +// 1) convert Float to multiprecision decimal +// 2) round to desired precision +// 3) read digits out and format + +package big + +import "strconv" + +// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner. + +// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats. +func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { + // TODO(gri) handle Inf. + + // 1) convert Float to multiprecision decimal + var d decimal + d.init(f.mant, int(f.exp)-f.mant.bitLen()) + + // 2) round to desired precision + shortest := false + if prec < 0 { + shortest = true + panic("unimplemented") + // TODO(gri) complete this + // roundShortest(&d, f.mant, int(f.exp)) + // Precision for shortest representation mode. + switch fmt { + case 'e', 'E': + prec = len(d.mant) - 1 + case 'f': + prec = max(len(d.mant)-d.exp, 0) + case 'g', 'G': + prec = len(d.mant) + } + } else { + // round appropriately + switch fmt { + case 'e', 'E': + // one digit before and number of digits after decimal point + d.round(1 + prec) + case 'f': + // number of digits before and after decimal point + d.round(d.exp + prec) + case 'g', 'G': + if prec == 0 { + prec = 1 + } + d.round(prec) + } + } + + // 3) read digits out and format + switch fmt { + case 'e', 'E': + return fmtE(buf, fmt, prec, f.neg, d) + case 'f': + return fmtF(buf, prec, f.neg, d) + case 'g', 'G': + // trim trailing fractional zeros in %e format + eprec := prec + if eprec > len(d.mant) && len(d.mant) >= d.exp { + eprec = len(d.mant) + } + // %e is used if the exponent from the conversion + // is less than -4 or greater than or equal to the precision. + // If precision was the shortest possible, use eprec = 6 for + // this decision. + if shortest { + eprec = 6 + } + exp := d.exp - 1 + if exp < -4 || exp >= eprec { + if prec > len(d.mant) { + prec = len(d.mant) + } + return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d) + } + if prec > d.exp { + prec = len(d.mant) + } + return fmtF(buf, max(prec-d.exp, 0), f.neg, d) + } + + // unknown format + return append(buf, '%', fmt) +} + +// %e: -d.ddddde±dd +func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte { + // sign + if neg { + buf = append(buf, '-') + } + + // first digit + ch := byte('0') + if len(d.mant) > 0 { + ch = d.mant[0] + } + buf = append(buf, ch) + + // .moredigits + if prec > 0 { + buf = append(buf, '.') + i := 1 + m := min(len(d.mant), prec+1) + if i < m { + buf = append(buf, d.mant[i:m]...) + i = m + } + for ; i <= prec; i++ { + buf = append(buf, '0') + } + } + + // e± + buf = append(buf, fmt) + var exp int64 + if len(d.mant) > 0 { + exp = int64(d.exp) - 1 // -1 because first digit was printed before '.' + } + if exp < 0 { + ch = '-' + exp = -exp + } else { + ch = '+' + } + buf = append(buf, ch) + + // dd...d + if exp < 10 { + buf = append(buf, '0') // at least 2 exponent digits + } + return strconv.AppendInt(buf, exp, 10) +} + +// %f: -ddddddd.ddddd +func fmtF(buf []byte, prec int, neg bool, d decimal) []byte { + // sign + if neg { + buf = append(buf, '-') + } + + // integer, padded with zeros as needed + if d.exp > 0 { + m := min(len(d.mant), d.exp) + buf = append(buf, d.mant[:m]...) + for ; m < d.exp; m++ { + buf = append(buf, '0') + } + } else { + buf = append(buf, '0') + } + + // fraction + if prec > 0 { + buf = append(buf, '.') + for i := 0; i < prec; i++ { + ch := byte('0') + if j := d.exp + i; 0 <= j && j < len(d.mant) { + ch = d.mant[j] + } + buf = append(buf, ch) + } + } + + return buf +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go index 1418dca335..cb8c76bed1 100644 --- a/src/math/big/int_test.go +++ b/src/math/big/int_test.go @@ -655,7 +655,7 @@ var primes = []string{ "10953742525620032441", "17908251027575790097", - // http://code.google.com/p/go/issues/detail?id=638 + // http://golang.org/issue/638 "18699199384836356663", "98920366548084643601728869055592650835572950932266967461790948584315647051443", diff --git a/src/nacltest.bash b/src/nacltest.bash index 6220d39f13..534f1ef5af 100755 --- a/src/nacltest.bash +++ b/src/nacltest.bash @@ -62,7 +62,7 @@ fi # Run host build to get toolchain for running zip generator. unset GOOS GOARCH if [ ! -f make.bash ]; then - echo 'nacl.bash must be run from $GOROOT/src' 1>&2 + echo 'nacltest.bash must be run from $GOROOT/src' 1>&2 exit 1 fi GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH ./make.bash diff --git a/src/net/dial.go b/src/net/dial.go index e6f0436cdd..0424ed250f 100644 --- a/src/net/dial.go +++ b/src/net/dial.go @@ -159,13 +159,15 @@ func (d *Dialer) Dial(network, address string) (Conn, error) { if err != nil { return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err} } - dialer := func(deadline time.Time) (Conn, error) { - return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline) - } + var dialer func(deadline time.Time) (Conn, error) if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" { dialer = func(deadline time.Time) (Conn, error) { return dialMulti(network, address, d.LocalAddr, ras, deadline) } + } else { + dialer = func(deadline time.Time) (Conn, error) { + return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline) + } } c, err := dial(network, ra.toAddr(), dialer, d.deadline()) if d.KeepAlive > 0 && err == nil { diff --git a/src/net/hosts_test.go b/src/net/hosts_test.go index 2fe358e079..5bb663b4c7 100644 --- a/src/net/hosts_test.go +++ b/src/net/hosts_test.go @@ -53,7 +53,7 @@ func TestLookupStaticHost(t *testing.T) { hostsPath = p } -// https://code.google.com/p/go/issues/detail?id=6646 +// https://golang.org/issue/6646 func TestSingleLineHostsFile(t *testing.T) { p := hostsPath hostsPath = "testdata/hosts_singleline" diff --git a/src/net/http/proxy_test.go b/src/net/http/proxy_test.go index b6aed3792b..823d1447ee 100644 --- a/src/net/http/proxy_test.go +++ b/src/net/http/proxy_test.go @@ -18,7 +18,7 @@ var UseProxyTests = []struct { match bool }{ // Never proxy localhost: - {"localhost:80", false}, + {"localhost", false}, {"127.0.0.1", false}, {"127.0.0.2", false}, {"[::1]", false}, diff --git a/src/net/http/request.go b/src/net/http/request.go index 63d7d44aa0..f7a7f19b9b 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -673,7 +673,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { // MaxBytesReader is similar to io.LimitReader but is intended for // limiting the size of incoming request bodies. In contrast to // io.LimitReader, MaxBytesReader's result is a ReadCloser, returns a -// non-EOF error for a Read beyond the limit, and Closes the +// non-EOF error for a Read beyond the limit, and closes the // underlying reader when its Close method is called. // // MaxBytesReader prevents clients from accidentally or maliciously diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 85d5705137..c21b57b57e 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -416,7 +416,7 @@ func TestServeMuxHandlerRedirects(t *testing.T) { } } -// Tests for http://code.google.com/p/go/issues/detail?id=900 +// Tests for http://golang.org/issue/900 func TestMuxRedirectLeadingSlashes(t *testing.T) { paths := []string{"//foo.txt", "///foo.txt", "/../../foo.txt"} for _, path := range paths { @@ -2124,7 +2124,7 @@ func TestDoubleHijack(t *testing.T) { <-conn.closec } -// http://code.google.com/p/go/issues/detail?id=5955 +// http://golang.org/issue/5955 // Note that this does not test the "request too large" // exit path from the http server. This is intentional; // not sending Connection: close is just a minor wire diff --git a/src/net/interface_test.go b/src/net/interface_test.go index fbf15de4d3..15c0cd7be4 100644 --- a/src/net/interface_test.go +++ b/src/net/interface_test.go @@ -38,8 +38,7 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string { return "" } for _, ifa := range ifat { - switch ifa := ifa.(type) { - case *IPNet: + if ifa, ok := ifa.(*IPNet); ok { if ifa.IP.To4() == nil && ifa.IP.IsLinkLocalUnicast() { return ifa.IP.String() } @@ -49,10 +48,6 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string { } func TestInterfaces(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("temporarily disabled until golang.org/issue/5395 is fixed") - } - ift, err := Interfaces() if err != nil { t.Fatal(err) @@ -110,10 +105,6 @@ func TestInterfaces(t *testing.T) { } func TestInterfaceAddrs(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("temporarily disabled until golang.org/issue/5395 is fixed") - } - ift, err := Interfaces() if err != nil { t.Fatal(err) diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go index 0759dc255d..438dc874d6 100644 --- a/src/net/interface_windows.go +++ b/src/net/interface_windows.go @@ -5,123 +5,139 @@ package net import ( + "internal/syscall/windows" "os" "syscall" "unsafe" ) -func bytePtrToString(p *uint8) string { - a := (*[10000]uint8)(unsafe.Pointer(p)) - i := 0 - for a[i] != 0 { - i++ - } - return string(a[:i]) -} +func getAdapters() (*windows.IpAdapterAddresses, error) { + block := uint32(unsafe.Sizeof(windows.IpAdapterAddresses{})) -func getAdapterList() (*syscall.IpAdapterInfo, error) { - b := make([]byte, 1000) - l := uint32(len(b)) - a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) - // TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that - // contains IPv4 address list only. We should use another API - // for fetching IPv6 stuff from the kernel. - err := syscall.GetAdaptersInfo(a, &l) - if err == syscall.ERROR_BUFFER_OVERFLOW { - b = make([]byte, l) - a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) - err = syscall.GetAdaptersInfo(a, &l) - } - if err != nil { - return nil, os.NewSyscallError("GetAdaptersInfo", err) + // pre-allocate a 15KB working buffer pointed to by the AdapterAddresses + // parameter. + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx + size := uint32(15000) + + var addrs []windows.IpAdapterAddresses + for { + addrs = make([]windows.IpAdapterAddresses, size/block+1) + err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, &addrs[0], &size) + if err == nil { + break + } + if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW { + return nil, os.NewSyscallError("GetAdaptersAddresses", err) + } } - return a, nil + return &addrs[0], nil } -func getInterfaceList() ([]syscall.InterfaceInfo, error) { +func getInterfaceInfos() ([]syscall.InterfaceInfo, error) { s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return nil, os.NewSyscallError("Socket", err) } defer syscall.Closesocket(s) - ii := [20]syscall.InterfaceInfo{} + iia := [20]syscall.InterfaceInfo{} ret := uint32(0) - size := uint32(unsafe.Sizeof(ii)) - err = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0) + size := uint32(unsafe.Sizeof(iia)) + err = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&iia[0])), size, &ret, nil, 0) if err != nil { return nil, os.NewSyscallError("WSAIoctl", err) } - c := ret / uint32(unsafe.Sizeof(ii[0])) - return ii[:c-1], nil + iilen := ret / uint32(unsafe.Sizeof(iia[0])) + return iia[:iilen-1], nil +} + +func bytesEqualIP(a []byte, b []int8) bool { + for i := 0; i < len(a); i++ { + if a[i] != byte(b[i]) { + return false + } + } + return true +} + +func findInterfaceInfo(iis []syscall.InterfaceInfo, paddr *windows.IpAdapterAddresses) *syscall.InterfaceInfo { + for _, ii := range iis { + iaddr := (*syscall.RawSockaddr)(unsafe.Pointer(&ii.Address)) + puni := paddr.FirstUnicastAddress + for ; puni != nil; puni = puni.Next { + if iaddr.Family == puni.Address.Sockaddr.Addr.Family { + switch iaddr.Family { + case syscall.AF_INET: + a := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr + if bytesEqualIP(a[:], puni.Address.Sockaddr.Addr.Data[2:]) { + return &ii + } + case syscall.AF_INET6: + a := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&ii.Address)).Addr + if bytesEqualIP(a[:], puni.Address.Sockaddr.Addr.Data[2:]) { + return &ii + } + default: + continue + } + } + } + } + return nil } // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otherwise it returns a mapping of a specific // interface. func interfaceTable(ifindex int) ([]Interface, error) { - ai, err := getAdapterList() + paddr, err := getAdapters() if err != nil { return nil, err } - ii, err := getInterfaceList() + iis, err := getInterfaceInfos() if err != nil { return nil, err } var ift []Interface - for ; ai != nil; ai = ai.Next { - index := ai.Index + for ; paddr != nil; paddr = paddr.Next { + index := paddr.IfIndex + if paddr.Ipv6IfIndex != 0 { + index = paddr.Ipv6IfIndex + } if ifindex == 0 || ifindex == int(index) { + ii := findInterfaceInfo(iis, paddr) + if ii == nil { + continue + } var flags Flags - - row := syscall.MibIfRow{Index: index} - e := syscall.GetIfEntry(&row) - if e != nil { - return nil, os.NewSyscallError("GetIfEntry", e) + if paddr.Flags&windows.IfOperStatusUp != 0 { + flags |= FlagUp } - - for _, ii := range ii { - ip := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr - ipv4 := IPv4(ip[0], ip[1], ip[2], ip[3]) - ipl := &ai.IpAddressList - for ipl != nil { - ips := bytePtrToString(&ipl.IpAddress.String[0]) - if ipv4.Equal(parseIPv4(ips)) { - break - } - ipl = ipl.Next - } - if ipl == nil { - continue - } - if ii.Flags&syscall.IFF_UP != 0 { - flags |= FlagUp - } - if ii.Flags&syscall.IFF_LOOPBACK != 0 { - flags |= FlagLoopback - } - if ii.Flags&syscall.IFF_BROADCAST != 0 { - flags |= FlagBroadcast - } - if ii.Flags&syscall.IFF_POINTTOPOINT != 0 { - flags |= FlagPointToPoint - } - if ii.Flags&syscall.IFF_MULTICAST != 0 { - flags |= FlagMulticast - } + if paddr.IfType&windows.IF_TYPE_SOFTWARE_LOOPBACK != 0 { + flags |= FlagLoopback + } + if ii.Flags&syscall.IFF_BROADCAST != 0 { + flags |= FlagBroadcast + } + if ii.Flags&syscall.IFF_POINTTOPOINT != 0 { + flags |= FlagPointToPoint + } + if ii.Flags&syscall.IFF_MULTICAST != 0 { + flags |= FlagMulticast } - - name := bytePtrToString(&ai.AdapterName[0]) - ifi := Interface{ Index: int(index), - MTU: int(row.Mtu), - Name: name, - HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]), - Flags: flags} + MTU: int(paddr.Mtu), + Name: syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(paddr.FriendlyName)))[:]), + HardwareAddr: HardwareAddr(paddr.PhysicalAddress[:]), + Flags: flags, + } ift = append(ift, ifi) + if ifindex == int(ifi.Index) { + break + } } } return ift, nil @@ -131,28 +147,86 @@ func interfaceTable(ifindex int) ([]Interface, error) { // network interfaces. Otherwise it returns addresses for a specific // interface. func interfaceAddrTable(ifi *Interface) ([]Addr, error) { - ai, err := getAdapterList() + paddr, err := getAdapters() if err != nil { return nil, err } var ifat []Addr - for ; ai != nil; ai = ai.Next { - index := ai.Index + for ; paddr != nil; paddr = paddr.Next { + index := paddr.IfIndex + if paddr.Ipv6IfIndex != 0 { + index = paddr.Ipv6IfIndex + } if ifi == nil || ifi.Index == int(index) { - ipl := &ai.IpAddressList - for ; ipl != nil; ipl = ipl.Next { - ifa := IPAddr{IP: parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))} - ifat = append(ifat, ifa.toAddr()) + puni := paddr.FirstUnicastAddress + for ; puni != nil; puni = puni.Next { + if sa, err := puni.Address.Sockaddr.Sockaddr(); err == nil { + switch sav := sa.(type) { + case *syscall.SockaddrInet4: + ifa := &IPNet{IP: make(IP, IPv4len), Mask: CIDRMask(int(puni.Address.SockaddrLength), 8*IPv4len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa) + case *syscall.SockaddrInet6: + ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(puni.Address.SockaddrLength), 8*IPv6len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa) + } + } + } + pany := paddr.FirstAnycastAddress + for ; pany != nil; pany = pany.Next { + if sa, err := pany.Address.Sockaddr.Sockaddr(); err == nil { + switch sav := sa.(type) { + case *syscall.SockaddrInet4: + ifa := &IPNet{IP: make(IP, IPv4len), Mask: CIDRMask(int(pany.Address.SockaddrLength), 8*IPv4len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa) + case *syscall.SockaddrInet6: + ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(pany.Address.SockaddrLength), 8*IPv6len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa) + } + } } } } + return ifat, nil } // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - // TODO(mikio): Implement this like other platforms. - return nil, nil + paddr, err := getAdapters() + if err != nil { + return nil, err + } + + var ifat []Addr + for ; paddr != nil; paddr = paddr.Next { + index := paddr.IfIndex + if paddr.Ipv6IfIndex != 0 { + index = paddr.Ipv6IfIndex + } + if ifi == nil || ifi.Index == int(index) { + pmul := paddr.FirstMulticastAddress + for ; pmul != nil; pmul = pmul.Next { + if sa, err := pmul.Address.Sockaddr.Sockaddr(); err == nil { + switch sav := sa.(type) { + case *syscall.SockaddrInet4: + ifa := &IPAddr{IP: make(IP, IPv4len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa.toAddr()) + case *syscall.SockaddrInet6: + ifa := &IPAddr{IP: make(IP, IPv6len)} + copy(ifa.IP, sav.Addr[:]) + ifat = append(ifat, ifa.toAddr()) + } + } + } + } + } + + return ifat, nil } diff --git a/src/net/net.go b/src/net/net.go index c850d2b1fd..339c972906 100644 --- a/src/net/net.go +++ b/src/net/net.go @@ -135,6 +135,8 @@ func (c *conn) Close() error { } // LocalAddr returns the local network address. +// The Addr returned is shared by all invocations of LocalAddr, so +// do not modify it. func (c *conn) LocalAddr() Addr { if !c.ok() { return nil @@ -143,6 +145,8 @@ func (c *conn) LocalAddr() Addr { } // RemoteAddr returns the remote network address. +// The Addr returned is shared by all invocations of RemoteAddr, so +// do not modify it. func (c *conn) RemoteAddr() Addr { if !c.ok() { return nil diff --git a/src/net/smtp/example_test.go b/src/net/smtp/example_test.go index d551e365a9..16419f4276 100644 --- a/src/net/smtp/example_test.go +++ b/src/net/smtp/example_test.go @@ -46,14 +46,36 @@ func Example() { } } +// variables to make ExamplePlainAuth compile, without adding +// unnecessary noise there. +var ( + from = "gopher@example.net" + msg = []byte("dummy message") + recipients = []string{"foo@example.com"} +) + func ExamplePlainAuth() { + // hostname is used by PlainAuth to validate the TLS certificate. + hostname := "mail.example.com" + auth := smtp.PlainAuth("", "user@example.com", "password", hostname) + + err := smtp.SendMail(hostname+":25", auth, from, recipients, msg) + if err != nil { + log.Fatal(err) + } +} + +func ExampleSendMail() { // Set up authentication information. auth := smtp.PlainAuth("", "user@example.com", "password", "mail.example.com") // Connect to the server, authenticate, set the sender and recipient, // and send the email all in one step. to := []string{"recipient@example.net"} - msg := []byte("This is the email body.") + msg := []byte("To: recipient@example.net\r\n" + + "Subject: discount Gophers!\r\n" + + "\r\n" + + "This is the email body.\r\n") err := smtp.SendMail("mail.example.com:25", auth, "sender@example.org", to, msg) if err != nil { log.Fatal(err) diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go index c9b3c07aa8..81f3c0bd62 100644 --- a/src/net/smtp/smtp.go +++ b/src/net/smtp/smtp.go @@ -264,9 +264,9 @@ func (d *dataCloser) Close() error { } // Data issues a DATA command to the server and returns a writer that -// can be used to write the data. The caller should close the writer -// before calling any more methods on c. -// A call to Data must be preceded by one or more calls to Rcpt. +// can be used to write the mail headers and body. The caller should +// close the writer before calling any more methods on c. A call to +// Data must be preceded by one or more calls to Rcpt. func (c *Client) Data() (io.WriteCloser, error) { _, _, err := c.cmd(354, "DATA") if err != nil { @@ -281,6 +281,21 @@ var testHookStartTLS func(*tls.Config) // nil, except for tests // possible, authenticates with the optional mechanism a if possible, // and then sends an email from address from, to addresses to, with // message msg. +// +// The addresses in the to parameter are the SMTP RCPT addresses. +// +// The msg parameter should be an RFC 822-style email with headers +// first, a blank line, and then the message body. The lines of msg +// should be CRLF terminated. The msg headers should usually include +// fields such as "From", "To", "Subject", and "Cc". Sending "Bcc" +// messages is accomplished by including an email address in the to +// parameter but not including it in the msg headers. +// +// The SendMail function and the the net/smtp package are low-level +// mechanisms and provide no support for DKIM signing, MIME +// attachments (see the mime/multipart package), or other mail +// functionality. Higher-level packages exist outside of the standard +// library. func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { c, err := Dial(addr) if err != nil { diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go index 52019d7b4e..ae2194277d 100644 --- a/src/net/tcpsock_plan9.go +++ b/src/net/tcpsock_plan9.go @@ -157,6 +157,8 @@ func (l *TCPListener) Close() error { } // Addr returns the listener's network address, a *TCPAddr. +// The Addr returned is shared by all invocations of Addr, so +// do not modify it. func (l *TCPListener) Addr() Addr { return l.fd.laddr } // SetDeadline sets the deadline associated with the listener. diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go index dd78aefa77..aaff0acaa6 100644 --- a/src/net/tcpsock_posix.go +++ b/src/net/tcpsock_posix.go @@ -258,6 +258,8 @@ func (l *TCPListener) Close() error { } // Addr returns the listener's network address, a *TCPAddr. +// The Addr returned is shared by all invocations of Addr, so +// do not modify it. func (l *TCPListener) Addr() Addr { return l.fd.laddr } // SetDeadline sets the deadline associated with the listener. diff --git a/src/net/udp_test.go b/src/net/udp_test.go index 125bbca6c4..2be2c319a7 100644 --- a/src/net/udp_test.go +++ b/src/net/udp_test.go @@ -81,26 +81,26 @@ func TestWriteToUDP(t *testing.T) { t.Skipf("skipping test on %q", runtime.GOOS) } - l, err := ListenPacket("udp", "127.0.0.1:0") + c, err := ListenPacket("udp", "127.0.0.1:0") if err != nil { - t.Fatalf("Listen failed: %v", err) + t.Fatal(err) } - defer l.Close() + defer c.Close() - testWriteToConn(t, l.LocalAddr().String()) - testWriteToPacketConn(t, l.LocalAddr().String()) + testWriteToConn(t, c.LocalAddr().String()) + testWriteToPacketConn(t, c.LocalAddr().String()) } func testWriteToConn(t *testing.T, raddr string) { c, err := Dial("udp", raddr) if err != nil { - t.Fatalf("Dial failed: %v", err) + t.Fatal(err) } defer c.Close() ra, err := ResolveUDPAddr("udp", raddr) if err != nil { - t.Fatalf("ResolveUDPAddr failed: %v", err) + t.Fatal(err) } _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra) @@ -121,36 +121,70 @@ func testWriteToConn(t *testing.T, raddr string) { _, err = c.Write([]byte("Connection-oriented mode socket")) if err != nil { - t.Fatalf("Write failed: %v", err) + t.Fatal(err) + } + + _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, ra) + if err == nil { + t.Fatal("WriteMsgUDP should fail") + } + if err != nil && err.(*OpError).Err != ErrWriteToConnected { + t.Fatalf("WriteMsgUDP should fail as ErrWriteToConnected: %v", err) + } + _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, nil) + switch runtime.GOOS { + case "nacl", "windows": // see golang.org/issue/9252 + t.Skipf("not implemented yet on %s", runtime.GOOS) + default: + if err != nil { + t.Fatal(err) + } } } func testWriteToPacketConn(t *testing.T, raddr string) { c, err := ListenPacket("udp", "127.0.0.1:0") if err != nil { - t.Fatalf("ListenPacket failed: %v", err) + t.Fatal(err) } defer c.Close() ra, err := ResolveUDPAddr("udp", raddr) if err != nil { - t.Fatalf("ResolveUDPAddr failed: %v", err) + t.Fatal(err) } _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra) if err != nil { - t.Fatalf("WriteToUDP failed: %v", err) + t.Fatal(err) } _, err = c.WriteTo([]byte("Connection-less mode socket"), ra) if err != nil { - t.Fatalf("WriteTo failed: %v", err) + t.Fatal(err) } _, err = c.(*UDPConn).Write([]byte("Connection-less mode socket")) if err == nil { t.Fatal("Write should fail") } + + _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, nil) + if err == nil { + t.Fatal("WriteMsgUDP should fail") + } + if err != nil && err.(*OpError).Err != errMissingAddress { + t.Fatalf("WriteMsgUDP should fail as errMissingAddress: %v", err) + } + _, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, ra) + switch runtime.GOOS { + case "nacl", "windows": // see golang.org/issue/9252 + t.Skipf("not implemented yet on %s", runtime.GOOS) + default: + if err != nil { + t.Fatal(err) + } + } } var udpConnLocalNameTests = []struct { diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go index 510ac5e4aa..269272632a 100644 --- a/src/net/udpsock_plan9.go +++ b/src/net/udpsock_plan9.go @@ -101,9 +101,11 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { return c.WriteToUDP(b, a) } -// WriteMsgUDP writes a packet to addr via c, copying the payload from -// b and the associated out-of-band data from oob. It returns the -// number of payload and out-of-band bytes written. +// WriteMsgUDP writes a packet to addr via c if c isn't connected, or +// to c's remote destination address if c is connected (in which case +// addr must be nil). The payload is copied from b and the associated +// out-of-band data is copied from oob. It returns the number of +// payload and out-of-band bytes written. func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { return 0, 0, syscall.EPLAN9 } diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go index a0533366a4..0770b7c5ce 100644 --- a/src/net/udpsock_posix.go +++ b/src/net/udpsock_posix.go @@ -139,17 +139,19 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { return c.WriteToUDP(b, a) } -// WriteMsgUDP writes a packet to addr via c, copying the payload from -// b and the associated out-of-band data from oob. It returns the -// number of payload and out-of-band bytes written. +// WriteMsgUDP writes a packet to addr via c if c isn't connected, or +// to c's remote destination address if c is connected (in which case +// addr must be nil). The payload is copied from b and the associated +// out-of-band data is copied from oob. It returns the number of +// payload and out-of-band bytes written. func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { if !c.ok() { return 0, 0, syscall.EINVAL } - if c.fd.isConnected { + if c.fd.isConnected && addr != nil { return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} } - if addr == nil { + if !c.fd.isConnected && addr == nil { return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} } sa, err := addr.sockaddr(c.fd.family) diff --git a/src/net/unixsock_plan9.go b/src/net/unixsock_plan9.go index c60c1d83bb..64a511d648 100644 --- a/src/net/unixsock_plan9.go +++ b/src/net/unixsock_plan9.go @@ -115,6 +115,8 @@ func (l *UnixListener) Close() error { } // Addr returns the listener's network address. +// The Addr returned is shared by all invocations of Addr, so +// do not modify it. func (l *UnixListener) Addr() Addr { return nil } // SetDeadline sets the deadline associated with the listener. diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go index 3c2e78bdca..d7127d9c69 100644 --- a/src/net/unixsock_posix.go +++ b/src/net/unixsock_posix.go @@ -321,6 +321,8 @@ func (l *UnixListener) Close() error { } // Addr returns the listener's network address. +// The Addr returned is shared by all invocations of Addr, so +// do not modify it. func (l *UnixListener) Addr() Addr { return l.fd.laddr } // SetDeadline sets the deadline associated with the listener. diff --git a/src/os/os_test.go b/src/os/os_test.go index a30a2b0313..5285b76024 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -21,7 +21,6 @@ import ( "sync" "syscall" "testing" - "text/template" "time" ) @@ -491,6 +490,30 @@ func TestReaddirStatFailures(t *testing.T) { } } +// Readdir on a regular file should fail. +func TestReaddirOfFile(t *testing.T) { + f, err := ioutil.TempFile("", "_Go_ReaddirOfFile") + if err != nil { + t.Fatal(err) + } + defer Remove(f.Name()) + f.Write([]byte("foo")) + f.Close() + reg, err := Open(f.Name()) + if err != nil { + t.Fatal(err) + } + defer reg.Close() + + names, err := reg.Readdirnames(-1) + if err == nil { + t.Error("Readdirnames succeeded; want non-nil error") + } + if len(names) > 0 { + t.Errorf("unexpected dir names in regular file: %q", names) + } +} + func TestHardLink(t *testing.T) { // Hardlinks are not supported under windows or Plan 9. if runtime.GOOS == "plan9" { @@ -922,7 +945,7 @@ func TestSeek(t *testing.T) { if off != tt.out || err != nil { if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 { // Reiserfs rejects the big seeks. - // http://code.google.com/p/go/issues/detail?id=91 + // http://golang.org/issue/91 break } t.Errorf("#%d: Seek(%v, %v) = %v, %v want %v, nil", i, tt.in, tt.whence, off, err, tt.out) @@ -1304,39 +1327,9 @@ func testKillProcess(t *testing.T, processKiller func(p *Process)) { t.Skipf("skipping on %s", runtime.GOOS) } - dir, err := ioutil.TempDir("", "go-build") - if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) - } - defer RemoveAll(dir) - - src := filepath.Join(dir, "main.go") - f, err := Create(src) - if err != nil { - t.Fatalf("Failed to create %v: %v", src, err) - } - st := template.Must(template.New("source").Parse(` -package main -import "time" -func main() { - time.Sleep(time.Second) -} -`)) - err = st.Execute(f, nil) - if err != nil { - f.Close() - t.Fatalf("Failed to execute template: %v", err) - } - f.Close() - - exe := filepath.Join(dir, "main.exe") - output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput() - if err != nil { - t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output)) - } - - cmd := osexec.Command(exe) - err = cmd.Start() + // Re-exec the test binary itself to emulate "sleep 1". + cmd := osexec.Command(Args[0], "-test.run", "TestSleep") + err := cmd.Start() if err != nil { t.Fatalf("Failed to start test process: %v", err) } @@ -1350,6 +1343,15 @@ func main() { } } +// TestSleep emulates "sleep 1". It is a helper for testKillProcess, so we +// don't have to rely on an external "sleep" command being available. +func TestSleep(t *testing.T) { + if testing.Short() { + t.Skip("Skipping in short mode") + } + time.Sleep(time.Second) +} + func TestKillStartProcess(t *testing.T) { testKillProcess(t, func(p *Process) { err := p.Kill() diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index fd96713eac..3ea0fc7f4f 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -3,11 +3,15 @@ package os_test import ( "io/ioutil" "os" + osexec "os/exec" "path/filepath" + "strings" "syscall" "testing" ) +var supportJunctionLinks = true + func init() { tmpdir, err := ioutil.TempDir("", "symtest") if err != nil { @@ -16,14 +20,18 @@ func init() { defer os.RemoveAll(tmpdir) err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) - if err == nil { - return + if err != nil { + err = err.(*os.LinkError).Err + switch err { + case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: + supportsSymlinks = false + } } + defer os.Remove("target") - err = err.(*os.LinkError).Err - switch err { - case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: - supportsSymlinks = false + b, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output() + if !strings.Contains(string(b), " /J ") { + supportJunctionLinks = false } } @@ -79,3 +87,33 @@ func TestSameWindowsFile(t *testing.T) { t.Errorf("files should be same") } } + +func TestStatJunctionLink(t *testing.T) { + if !supportJunctionLinks { + t.Skip("skipping because junction links are not supported") + } + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + link := filepath.Join(filepath.Dir(dir), filepath.Base(dir)+"-link") + + output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, dir).CombinedOutput() + if err != nil { + t.Fatalf("failed to run mklink %v %v: %v %q", link, dir, err, output) + } + defer os.Remove(link) + + fi, err := os.Stat(link) + if err != nil { + t.Fatalf("failed to stat link %v: %v", link, err) + } + expected := filepath.Base(dir) + got := fi.Name() + if !fi.IsDir() || expected != got { + t.Fatalf("link should point to %v but points to %v instead", expected, got) + } +} diff --git a/src/os/signal/signal.go b/src/os/signal/signal.go index 3004275495..81906d6f40 100644 --- a/src/os/signal/signal.go +++ b/src/os/signal/signal.go @@ -5,8 +5,6 @@ // Package signal implements access to incoming signals. package signal -// BUG(rsc): This package is not yet implemented on Plan 9. - import ( "os" "sync" diff --git a/src/os/signal/signal_plan9.go b/src/os/signal/signal_plan9.go new file mode 100644 index 0000000000..45355da48a --- /dev/null +++ b/src/os/signal/signal_plan9.go @@ -0,0 +1,55 @@ +// 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 signal + +import ( + "os" + "syscall" +) + +var sigtab = make(map[os.Signal]int) + +// In sig.s; jumps to runtime. +func signal_disable(uint32) +func signal_enable(uint32) +func signal_recv() string + +func init() { + signal_enable(0) // first call - initialize + go loop() +} + +func loop() { + for { + process(syscall.Note(signal_recv())) + } +} + +const numSig = 256 + +func signum(sig os.Signal) int { + switch sig := sig.(type) { + case syscall.Note: + n, ok := sigtab[sig] + if !ok { + n = len(sigtab) + 1 + if n > numSig { + return -1 + } + sigtab[sig] = n + } + return n + default: + return -1 + } +} + +func enableSignal(sig int) { + signal_enable(uint32(sig)) +} + +func disableSignal(sig int) { + signal_disable(uint32(sig)) +} diff --git a/src/os/signal/signal_plan9_test.go b/src/os/signal/signal_plan9_test.go new file mode 100644 index 0000000000..10bfdc3ff1 --- /dev/null +++ b/src/os/signal/signal_plan9_test.go @@ -0,0 +1,181 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package signal + +import ( + "os" + "runtime" + "syscall" + "testing" + "time" +) + +func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { + select { + case s := <-c: + if s != sig { + t.Fatalf("signal was %v, want %v", s, sig) + } + case <-time.After(1 * time.Second): + t.Fatalf("timeout waiting for %v", sig) + } +} + +// Test that basic signal handling works. +func TestSignal(t *testing.T) { + // Ask for hangup + c := make(chan os.Signal, 1) + Notify(c, syscall.Note("hangup")) + defer Stop(c) + + // Send this process a hangup + t.Logf("hangup...") + postNote(syscall.Getpid(), "hangup") + waitSig(t, c, syscall.Note("hangup")) + + // Ask for everything we can get. + c1 := make(chan os.Signal, 1) + Notify(c1) + + // Send this process an alarm + t.Logf("alarm...") + postNote(syscall.Getpid(), "alarm") + waitSig(t, c1, syscall.Note("alarm")) + + // Send two more hangups, to make sure that + // they get delivered on c1 and that not reading + // from c does not block everything. + t.Logf("hangup...") + postNote(syscall.Getpid(), "hangup") + waitSig(t, c1, syscall.Note("hangup")) + t.Logf("hangup...") + postNote(syscall.Getpid(), "hangup") + waitSig(t, c1, syscall.Note("hangup")) + + // The first SIGHUP should be waiting for us on c. + waitSig(t, c, syscall.Note("hangup")) +} + +func TestStress(t *testing.T) { + dur := 3 * time.Second + if testing.Short() { + dur = 100 * time.Millisecond + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + done := make(chan bool) + finished := make(chan bool) + go func() { + sig := make(chan os.Signal, 1) + Notify(sig, syscall.Note("alarm")) + defer Stop(sig) + Loop: + for { + select { + case <-sig: + case <-done: + break Loop + } + } + finished <- true + }() + go func() { + Loop: + for { + select { + case <-done: + break Loop + default: + postNote(syscall.Getpid(), "alarm") + runtime.Gosched() + } + } + finished <- true + }() + time.Sleep(dur) + close(done) + <-finished + <-finished + // When run with 'go test -cpu=1,2,4' alarm from this test can slip + // into subsequent TestSignal() causing failure. + // Sleep for a while to reduce the possibility of the failure. + time.Sleep(10 * time.Millisecond) +} + +// Test that Stop cancels the channel's registrations. +func TestStop(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + sigs := []string{ + "alarm", + "hangup", + } + + for _, sig := range sigs { + // Send the signal. + // If it's alarm, we should not see it. + // If it's hangup, maybe we'll die. Let the flag tell us what to do. + if sig != "hangup" { + postNote(syscall.Getpid(), sig) + } + time.Sleep(100 * time.Millisecond) + + // Ask for signal + c := make(chan os.Signal, 1) + Notify(c, syscall.Note(sig)) + defer Stop(c) + + // Send this process that signal + postNote(syscall.Getpid(), sig) + waitSig(t, c, syscall.Note(sig)) + + Stop(c) + select { + case s := <-c: + t.Fatalf("unexpected signal %v", s) + case <-time.After(100 * time.Millisecond): + // nothing to read - good + } + + // Send the signal. + // If it's alarm, we should not see it. + // If it's hangup, maybe we'll die. Let the flag tell us what to do. + if sig != "hangup" { + postNote(syscall.Getpid(), sig) + } + + select { + case s := <-c: + t.Fatalf("unexpected signal %v", s) + case <-time.After(100 * time.Millisecond): + // nothing to read - good + } + } +} + +func itoa(val int) string { + if val < 0 { + return "-" + itoa(-val) + } + var buf [32]byte // big enough for int64 + i := len(buf) - 1 + for val >= 10 { + buf[i] = byte(val%10 + '0') + i-- + val /= 10 + } + buf[i] = byte(val + '0') + return string(buf[i:]) +} + +func postNote(pid int, note string) error { + f, err := os.OpenFile("/proc/"+itoa(pid)+"/note", os.O_WRONLY, 0) + if err != nil { + return err + } + defer f.Close() + _, err = f.Write([]byte(note)) + return err +} diff --git a/src/os/signal/signal_stub.go b/src/os/signal/signal_stub.go deleted file mode 100644 index d0a6935ff2..0000000000 --- a/src/os/signal/signal_stub.go +++ /dev/null @@ -1,17 +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. - -// +build plan9 - -package signal - -import "os" - -const numSig = 0 - -func signum(sig os.Signal) int { return -1 } - -func disableSignal(int) {} - -func enableSignal(int) {} diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go index c4f74b97ff..293d316124 100644 --- a/src/path/filepath/path_test.go +++ b/src/path/filepath/path_test.go @@ -1008,7 +1008,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) { } } -func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486 +func TestBug3486(t *testing.T) { // http://golang.org/issue/3486 root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") if err != nil { t.Fatal(err) diff --git a/src/race.bat b/src/race.bat index 8858c57b06..027c475640 100644 --- a/src/race.bat +++ b/src/race.bat @@ -18,7 +18,7 @@ goto end set GOROOT=%CD%\.. call make.bat --dist-tool >NUL if errorlevel 1 goto fail -.\cmd\dist\dist env -wp >env.bat +.\cmd\dist\dist env -w -p >env.bat if errorlevel 1 goto fail call env.bat del env.bat diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 7d40f9a8b6..2be32f31b4 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -2735,6 +2735,8 @@ var tagGetTests = []struct { {`protobuf:"PB(1,2)"`, `rotobuf`, ``}, {`protobuf:"PB(1,2)" json:"name"`, `json`, `name`}, {`protobuf:"PB(1,2)" json:"name"`, `protobuf`, `PB(1,2)`}, + {`k0:"values contain spaces" k1:"and\ttabs"`, "k0", "values contain spaces"}, + {`k0:"values contain spaces" k1:"and\ttabs"`, "k1", "and\ttabs"}, } func TestTagGet(t *testing.T) { diff --git a/src/reflect/type.go b/src/reflect/type.go index ae7d165a68..0a8c40808a 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -762,8 +762,11 @@ type StructTag string // If the tag does not have the conventional format, the value // returned by Get is unspecified. func (tag StructTag) Get(key string) string { + // When modifying this code, also update the validateStructTag code + // in golang.org/x/tools/cmd/vet/structtag.go. + for tag != "" { - // skip leading space + // Skip leading space. i := 0 for i < len(tag) && tag[i] == ' ' { i++ @@ -773,19 +776,21 @@ func (tag StructTag) Get(key string) string { break } - // scan to colon. - // a space or a quote is a syntax error + // Scan to colon. A space, a quote or a control character is a syntax error. + // Strictly speaking, control chars include the range [0x7f, 0x9f], not just + // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters + // as it is simpler to inspect the tag's bytes than the tag's runes. i = 0 - for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { i++ } - if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { break } name := string(tag[:i]) tag = tag[i+1:] - // scan quoted string to find value + // Scan quoted string to find value. i = 1 for i < len(tag) && tag[i] != '"' { if tag[i] == '\\' { @@ -800,7 +805,10 @@ func (tag StructTag) Get(key string) string { tag = tag[i+1:] if key == name { - value, _ := strconv.Unquote(qvalue) + value, err := strconv.Unquote(qvalue) + if err != nil { + break + } return value } } diff --git a/src/reflect/value.go b/src/reflect/value.go index 4060206eac..081c4d9d7b 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -2439,12 +2439,27 @@ func chansend(t *rtype, ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool func makechan(typ *rtype, size uint64) (ch unsafe.Pointer) func makemap(t *rtype) (m unsafe.Pointer) + +//go:noescape func mapaccess(t *rtype, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) + func mapassign(t *rtype, m unsafe.Pointer, key, val unsafe.Pointer) + +//go:noescape func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer) + +// m escapes into the return value, but the caller of mapiterinit +// doesn't let the return value escape. +//go:noescape func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer + +//go:noescape func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer) + +//go:noescape func mapiternext(it unsafe.Pointer) + +//go:noescape func maplen(m unsafe.Pointer) int // call calls fn with a copy of the n argument bytes pointed at by arg. diff --git a/src/runtime/asm_386.s b/src/runtime/asm_386.s index 49bba32ebe..58a0d502bd 100644 --- a/src/runtime/asm_386.s +++ b/src/runtime/asm_386.s @@ -1298,26 +1298,22 @@ eq: RET // eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. // See runtime_test.go:eqstring_generic for // equivalent Go code. TEXT runtime·eqstring(SB),NOSPLIT,$0-17 - MOVL s1len+4(FP), AX - MOVL s2len+12(FP), BX - CMPL AX, BX - JNE different MOVL s1str+0(FP), SI MOVL s2str+8(FP), DI CMPL SI, DI JEQ same + MOVL s1len+4(FP), BX CALL runtime·memeqbody(SB) MOVB AX, v+16(FP) RET same: MOVB $1, v+16(FP) RET -different: - MOVB $0, v+16(FP) - RET TEXT bytes·Equal(SB),NOSPLIT,$0-25 MOVL a_len+4(FP), BX diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index f09e5ae250..f6c1c5f6e6 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1262,26 +1262,22 @@ eq: RET // eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. // See runtime_test.go:eqstring_generic for // equivalent Go code. TEXT runtime·eqstring(SB),NOSPLIT,$0-33 - MOVQ s1len+8(FP), AX - MOVQ s2len+24(FP), BX - CMPQ AX, BX - JNE noteq MOVQ s1str+0(FP), SI MOVQ s2str+16(FP), DI CMPQ SI, DI JEQ eq + MOVQ s1len+8(FP), BX CALL runtime·memeqbody(SB) MOVB AX, v+32(FP) RET eq: MOVB $1, v+32(FP) RET -noteq: - MOVB $0, v+32(FP) - RET // a in SI // b in DI diff --git a/src/runtime/asm_amd64p32.s b/src/runtime/asm_amd64p32.s index 77355bb998..f2324285a5 100644 --- a/src/runtime/asm_amd64p32.s +++ b/src/runtime/asm_amd64p32.s @@ -704,26 +704,22 @@ eq: RET // eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. // See runtime_test.go:eqstring_generic for // equivalent Go code. TEXT runtime·eqstring(SB),NOSPLIT,$0-17 - MOVL s1len+4(FP), AX - MOVL s2len+12(FP), BX - CMPL AX, BX - JNE different MOVL s1str+0(FP), SI MOVL s2str+8(FP), DI CMPL SI, DI JEQ same + MOVL s1len+4(FP), BX CALL runtime·memeqbody(SB) MOVB AX, v+16(FP) RET same: MOVB $1, v+16(FP) RET -different: - MOVB $0, v+16(FP) - RET // a in SI // b in DI diff --git a/src/runtime/asm_arm.s b/src/runtime/asm_arm.s index f3ce1a8f17..2efeaaa531 100644 --- a/src/runtime/asm_arm.s +++ b/src/runtime/asm_arm.s @@ -39,20 +39,8 @@ TEXT runtime·rt0_go(SB),NOSPLIT,$-4 BL runtime·emptyfunc(SB) // fault if stack check is wrong -#ifndef GOOS_nacl - // if there is an _cgo_init, call it. - MOVW _cgo_init(SB), R4 - CMP $0, R4 - B.EQ nocgo - MRC 15, 0, R0, C13, C0, 3 // load TLS base pointer - MOVW R0, R3 // arg 3: TLS base pointer - MOVW $runtime·tlsg(SB), R2 // arg 2: tlsg - MOVW $setg_gcc<>(SB), R1 // arg 1: setg - MOVW g, R0 // arg 0: G - BL (R4) // will clobber R0-R3 -#endif + BL runtime·_initcgo(SB) // will clobber R0-R3 -nocgo: // update stackguard after _cgo_init MOVW (g_stack+stack_lo)(g), R0 ADD $const__StackGuard, R0 @@ -806,21 +794,18 @@ eq: RET // eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. // See runtime_test.go:eqstring_generic for // equivalent Go code. TEXT runtime·eqstring(SB),NOSPLIT,$-4-17 - MOVW s1len+4(FP), R0 - MOVW s2len+12(FP), R1 - MOVW $0, R7 - CMP R0, R1 - MOVB.NE R7, v+16(FP) - RET.NE MOVW s1str+0(FP), R2 MOVW s2str+8(FP), R3 MOVW $1, R8 MOVB R8, v+16(FP) CMP R2, R3 RET.EQ + MOVW s1len+4(FP), R0 ADD R2, R0, R6 loop: CMP R2, R6 @@ -829,14 +814,10 @@ loop: MOVBU.P 1(R3), R5 CMP R4, R5 BEQ loop - MOVB R7, v+16(FP) + MOVW $0, R8 + MOVB R8, v+16(FP) RET -// void setg_gcc(G*); set g called from gcc. -TEXT setg_gcc<>(SB),NOSPLIT,$0 - MOVW R0, g - B runtime·save_g(SB) - // TODO: share code with memeq? TEXT bytes·Equal(SB),NOSPLIT,$0 MOVW a_len+4(FP), R1 diff --git a/src/runtime/asm_ppc64x.s b/src/runtime/asm_ppc64x.s index 660c7cdfe5..694dfcc24e 100644 --- a/src/runtime/asm_ppc64x.s +++ b/src/runtime/asm_ppc64x.s @@ -969,30 +969,30 @@ eq: RETURN // eqstring tests whether two strings are equal. +// The compiler guarantees that strings passed +// to eqstring have equal length. // See runtime_test.go:eqstring_generic for // equivalent Go code. TEXT runtime·eqstring(SB),NOSPLIT,$0-33 - MOVD s1len+8(FP), R4 - MOVD s2len+24(FP), R5 - CMP R4, R5 - BNE noteq - MOVD s1str+0(FP), R3 MOVD s2str+16(FP), R4 + MOVD $1, R5 + MOVB R5, ret+32(FP) + CMP R3, R4 + BNE 2(PC) + RETURN + MOVD s1len+8(FP), R5 SUB $1, R3 SUB $1, R4 ADD R3, R5, R8 loop: CMP R3, R8 - BNE 4(PC) - MOVD $1, R3 - MOVB R3, ret+32(FP) + BNE 2(PC) RETURN MOVBZU 1(R3), R6 MOVBZU 1(R4), R7 CMP R6, R7 BEQ loop -noteq: MOVB R0, ret+32(FP) RETURN diff --git a/src/runtime/cgo/cgo.go b/src/runtime/cgo/cgo.go index 8528692f7b..9a41399cd6 100644 --- a/src/runtime/cgo/cgo.go +++ b/src/runtime/cgo/cgo.go @@ -11,7 +11,7 @@ package cgo /* -#cgo darwin LDFLAGS: -lpthread +#cgo darwin,!arm LDFLAGS: -lpthread #cgo dragonfly LDFLAGS: -lpthread #cgo freebsd LDFLAGS: -lpthread #cgo android LDFLAGS: -llog diff --git a/src/runtime/cgo/gcc_arm.S b/src/runtime/cgo/gcc_arm.S index d5833bfad0..980ab579e4 100644 --- a/src/runtime/cgo/gcc_arm.S +++ b/src/runtime/cgo/gcc_arm.S @@ -11,6 +11,10 @@ #define EXT(s) s #endif +// Apple's ld64 wants 4-byte alignment for ARM code sections. +// .align in both Apple as and GNU as treat n as aligning to 2**n bytes. +.align 2 + /* * void crosscall_arm1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) * diff --git a/src/runtime/cgo/gcc_darwin_arm.c b/src/runtime/cgo/gcc_darwin_arm.c new file mode 100644 index 0000000000..d56c55777d --- /dev/null +++ b/src/runtime/cgo/gcc_darwin_arm.c @@ -0,0 +1,99 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <string.h> /* for strerror */ +#include <pthread.h> +#include <signal.h> +#include <limits.h> +#include "libcgo.h" + +#define magic (0xe696c4f4U) + +// inittls allocates a thread-local storage slot for g. +// +// It finds the first available slot using pthread_key_create and uses +// it as the offset value for runtime.tlsg. +static void +inittls(void **tlsg, void **tlsbase) +{ + pthread_key_t k; + int i, err; + + err = pthread_key_create(&k, nil); + if(err != 0) { + fprintf(stderr, "runtime/cgo: pthread_key_create failed: %d\n", err); + abort(); + } + //fprintf(stderr, "runtime/cgo: k = %d, tlsbase = %p\n", (int)k, tlsbase); // debug + pthread_setspecific(k, (void*)magic); + // The first key should be at 258. + for (i=0; i<PTHREAD_KEYS_MAX; i++) { + if (*(tlsbase+i) == (void*)magic) { + *tlsg = (void*)(i*sizeof(void *)); + pthread_setspecific(k, 0); + return; + } + } + fprintf(stderr, "runtime/cgo: could not find pthread key.\n"); + abort(); +} + +static void *threadentry(void*); +void (*setg_gcc)(void*); + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + size = 0; + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstack will do the rest. + ts->g->stackhi = size; + err = pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall_arm1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall_arm1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + pthread_attr_t attr; + size_t size; + + setg_gcc = setg; + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + g->stacklo = (uintptr)&attr - size + 4096; + pthread_attr_destroy(&attr); + + // yes, tlsbase from mrc might not be correctly aligned. + inittls(tlsg, (void**)((uintptr)tlsbase & ~3)); +} diff --git a/src/runtime/compiler.go b/src/runtime/compiler.go index 562a460226..f6edc95959 100644 --- a/src/runtime/compiler.go +++ b/src/runtime/compiler.go @@ -7,7 +7,7 @@ package runtime // Compiler is the name of the compiler toolchain that built the // running binary. Known toolchains are: // -// gc The 5g/6g/8g compiler suite at code.google.com/p/go. +// gc The 5g/6g/8g compiler suite at go.googlesource.com/go. // gccgo The gccgo front end, part of the GCC compiler suite. // const Compiler = "gc" diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 43cea9008a..715b2da232 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -10,6 +10,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" "strings" "sync" @@ -17,17 +18,20 @@ import ( "text/template" ) -// testEnv excludes GODEBUG from the environment -// to prevent its output from breaking tests that -// are trying to parse other command output. func testEnv(cmd *exec.Cmd) *exec.Cmd { if cmd.Env != nil { panic("environment already set") } for _, env := range os.Environ() { + // Exclude GODEBUG from the environment to prevent its output + // from breaking tests that are trying to parse other command output. if strings.HasPrefix(env, "GODEBUG=") { continue } + // Exclude GOTRACEBACK for the same reason. + if strings.HasPrefix(env, "GOTRACEBACK=") { + continue + } cmd.Env = append(cmd.Env, env) } return cmd @@ -217,6 +221,14 @@ func TestMainGoroutineId(t *testing.T) { } } +func TestNoHelperGoroutines(t *testing.T) { + output := executeTest(t, noHelperGoroutinesSource, nil) + matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1) + if len(matches) != 1 || matches[0][0] != "goroutine 1 [" { + t.Fatalf("want to see only goroutine 1, see:\n%s", output) + } +} + func TestBreakpoint(t *testing.T) { output := executeTest(t, breakpointSource, nil) want := "runtime.Breakpoint()" @@ -431,6 +443,22 @@ func main() { } ` +const noHelperGoroutinesSource = ` +package main +import ( + "runtime" + "time" +) +func init() { + i := 0 + runtime.SetFinalizer(&i, func(p *int) {}) + time.AfterFunc(time.Hour, func() {}) + panic("oops") +} +func main() { +} +` + const breakpointSource = ` package main import "runtime" diff --git a/src/runtime/defs_darwin_arm.go b/src/runtime/defs_darwin_arm.go new file mode 100644 index 0000000000..92bab509fb --- /dev/null +++ b/src/runtime/defs_darwin_arm.go @@ -0,0 +1,245 @@ +// Note: cgo can't handle some Darwin/ARM structures, so this file can't +// be auto generated by cgo yet. +// Created based on output of `cgo -cdefs defs_darwin.go` and Darwin/ARM +// specific header (mainly mcontext and ucontext related stuff) + +package runtime + +import "unsafe" + +const ( + _EINTR = 0x4 + _EFAULT = 0xe + + _PROT_NONE = 0x0 + _PROT_READ = 0x1 + _PROT_WRITE = 0x2 + _PROT_EXEC = 0x4 + + _MAP_ANON = 0x1000 + _MAP_PRIVATE = 0x2 + _MAP_FIXED = 0x10 + + _MADV_DONTNEED = 0x4 + _MADV_FREE = 0x5 + + _MACH_MSG_TYPE_MOVE_RECEIVE = 0x10 + _MACH_MSG_TYPE_MOVE_SEND = 0x11 + _MACH_MSG_TYPE_MOVE_SEND_ONCE = 0x12 + _MACH_MSG_TYPE_COPY_SEND = 0x13 + _MACH_MSG_TYPE_MAKE_SEND = 0x14 + _MACH_MSG_TYPE_MAKE_SEND_ONCE = 0x15 + _MACH_MSG_TYPE_COPY_RECEIVE = 0x16 + + _MACH_MSG_PORT_DESCRIPTOR = 0x0 + _MACH_MSG_OOL_DESCRIPTOR = 0x1 + _MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2 + _MACH_MSG_OOL_VOLATILE_DESCRIPTOR = 0x3 + + _MACH_MSGH_BITS_COMPLEX = 0x80000000 + + _MACH_SEND_MSG = 0x1 + _MACH_RCV_MSG = 0x2 + _MACH_RCV_LARGE = 0x4 + + _MACH_SEND_TIMEOUT = 0x10 + _MACH_SEND_INTERRUPT = 0x40 + _MACH_SEND_ALWAYS = 0x10000 + _MACH_SEND_TRAILER = 0x20000 + _MACH_RCV_TIMEOUT = 0x100 + _MACH_RCV_NOTIFY = 0x200 + _MACH_RCV_INTERRUPT = 0x400 + _MACH_RCV_OVERWRITE = 0x1000 + + _NDR_PROTOCOL_2_0 = 0x0 + _NDR_INT_BIG_ENDIAN = 0x0 + _NDR_INT_LITTLE_ENDIAN = 0x1 + _NDR_FLOAT_IEEE = 0x0 + _NDR_CHAR_ASCII = 0x0 + + _SA_SIGINFO = 0x40 + _SA_RESTART = 0x2 + _SA_ONSTACK = 0x1 + _SA_USERTRAMP = 0x100 + _SA_64REGSET = 0x200 + + _SIGHUP = 0x1 + _SIGINT = 0x2 + _SIGQUIT = 0x3 + _SIGILL = 0x4 + _SIGTRAP = 0x5 + _SIGABRT = 0x6 + _SIGEMT = 0x7 + _SIGFPE = 0x8 + _SIGKILL = 0x9 + _SIGBUS = 0xa + _SIGSEGV = 0xb + _SIGSYS = 0xc + _SIGPIPE = 0xd + _SIGALRM = 0xe + _SIGTERM = 0xf + _SIGURG = 0x10 + _SIGSTOP = 0x11 + _SIGTSTP = 0x12 + _SIGCONT = 0x13 + _SIGCHLD = 0x14 + _SIGTTIN = 0x15 + _SIGTTOU = 0x16 + _SIGIO = 0x17 + _SIGXCPU = 0x18 + _SIGXFSZ = 0x19 + _SIGVTALRM = 0x1a + _SIGPROF = 0x1b + _SIGWINCH = 0x1c + _SIGINFO = 0x1d + _SIGUSR1 = 0x1e + _SIGUSR2 = 0x1f + + _FPE_INTDIV = 0x7 + _FPE_INTOVF = 0x8 + _FPE_FLTDIV = 0x1 + _FPE_FLTOVF = 0x2 + _FPE_FLTUND = 0x3 + _FPE_FLTRES = 0x4 + _FPE_FLTINV = 0x5 + _FPE_FLTSUB = 0x6 + + _BUS_ADRALN = 0x1 + _BUS_ADRERR = 0x2 + _BUS_OBJERR = 0x3 + + _SEGV_MAPERR = 0x1 + _SEGV_ACCERR = 0x2 + + _ITIMER_REAL = 0x0 + _ITIMER_VIRTUAL = 0x1 + _ITIMER_PROF = 0x2 + + _EV_ADD = 0x1 + _EV_DELETE = 0x2 + _EV_CLEAR = 0x20 + _EV_RECEIPT = 0x40 + _EV_ERROR = 0x4000 + _EVFILT_READ = -0x1 + _EVFILT_WRITE = -0x2 +) + +type machbody struct { + msgh_descriptor_count uint32 +} + +type machheader struct { + msgh_bits uint32 + msgh_size uint32 + msgh_remote_port uint32 + msgh_local_port uint32 + msgh_reserved uint32 + msgh_id int32 +} + +type machndr struct { + mig_vers uint8 + if_vers uint8 + reserved1 uint8 + mig_encoding uint8 + int_rep uint8 + char_rep uint8 + float_rep uint8 + reserved2 uint8 +} + +type machport struct { + name uint32 + pad1 uint32 + pad2 uint16 + disposition uint8 + _type uint8 +} + +type stackt struct { + ss_sp *byte + ss_size uintptr + ss_flags int32 +} + +type sigactiont struct { + __sigaction_u [4]byte + sa_tramp unsafe.Pointer + sa_mask uint32 + sa_flags int32 +} + +type siginfo struct { + si_signo int32 + si_errno int32 + si_code int32 + si_pid int32 + si_uid uint32 + si_status int32 + si_addr *byte + si_value [4]byte + si_band int32 + __pad [7]uint32 +} + +type timeval struct { + tv_sec int32 + tv_usec int32 +} + +func (tv *timeval) set_usec(x int32) { + tv.tv_usec = x +} + +type itimerval struct { + it_interval timeval + it_value timeval +} + +type timespec struct { + tv_sec int32 + tv_nsec int32 +} + +type floatstate32 struct { + r [32]uint32 + fpscr uint32 +} + +type regs32 struct { + r [13]uint32 // r0 to r12 + sp uint32 // r13 + lr uint32 // r14 + pc uint32 // r15 + cpsr uint32 +} + +type exceptionstate32 struct { + trapno uint32 // NOTE: on 386, the trapno field is split into trapno and cpu + err uint32 + faultvaddr uint32 +} + +type mcontext32 struct { + es exceptionstate32 + ss regs32 + fs floatstate32 +} + +type ucontext struct { + uc_onstack int32 + uc_sigmask uint32 + uc_stack stackt + uc_link *ucontext + uc_mcsize uint32 + uc_mcontext *mcontext32 +} + +type keventt struct { + ident uint32 + filter int16 + flags uint16 + fflags uint32 + data int32 + udata *byte +} diff --git a/src/runtime/defs_windows_386.go b/src/runtime/defs_windows_386.go index abec2d839f..c860f74a3f 100644 --- a/src/runtime/defs_windows_386.go +++ b/src/runtime/defs_windows_386.go @@ -101,6 +101,12 @@ type context struct { extendedregisters [512]uint8 } +func (c *context) ip() uintptr { return uintptr(c.eip) } +func (c *context) sp() uintptr { return uintptr(c.esp) } + +func (c *context) setip(x uintptr) { c.eip = uint32(x) } +func (c *context) setsp(x uintptr) { c.esp = uint32(x) } + type overlapped struct { internal uint32 internalhigh uint32 diff --git a/src/runtime/defs_windows_amd64.go b/src/runtime/defs_windows_amd64.go index 81b13597b7..d1e55ec426 100644 --- a/src/runtime/defs_windows_amd64.go +++ b/src/runtime/defs_windows_amd64.go @@ -116,6 +116,12 @@ type context struct { lastexceptionfromrip uint64 } +func (c *context) ip() uintptr { return uintptr(c.rip) } +func (c *context) sp() uintptr { return uintptr(c.rsp) } + +func (c *context) setip(x uintptr) { c.rip = uint64(x) } +func (c *context) setsp(x uintptr) { c.rsp = uint64(x) } + type overlapped struct { internal uint64 internalhigh uint64 diff --git a/src/runtime/gcwork.go b/src/runtime/gcwork.go new file mode 100644 index 0000000000..cf5a97957f --- /dev/null +++ b/src/runtime/gcwork.go @@ -0,0 +1,338 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +const ( + _Debugwbufs = true // if true check wbufs consistency + _WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion. +) + +type workbufhdr struct { + node lfnode // must be first + nobj uintptr + id uintptr + inuse bool // This workbuf is in use by some gorotuine and is not on the work.empty/partial/full queues. + log [4]uintptr // line numbers forming a history of ownership changes to workbuf +} + +type workbuf struct { + workbufhdr + // account for the above fields + obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / ptrSize]uintptr +} + +// workbuf factory routines. These funcs are used to manage the +// workbufs. They cache workbuf in the m struct field currentwbuf. +// If the GC asks for some work these are the only routines that +// make partially full wbufs available to the GC. +// Each of the gets and puts also take an distinct integer that is used +// to record a brief history of changes to ownership of the workbuf. +// The convention is to use a unique line number but any encoding +// is permissible. For example if you want to pass in 2 bits of information +// you could simple add lineno1*100000+lineno2. + +// logget records the past few values of entry to aid in debugging. +// logget checks the buffer b is not currently in use. +func (b *workbuf) logget(entry uintptr) { + if !_Debugwbufs { + return + } + if b.inuse { + println("runtime: logget fails log entry=", entry, + "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], + "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) + throw("logget: get not legal") + } + b.inuse = true + copy(b.log[1:], b.log[:]) + b.log[0] = entry +} + +// logput records the past few values of entry to aid in debugging. +// logput checks the buffer b is currently in use. +func (b *workbuf) logput(entry uintptr) { + if !_Debugwbufs { + return + } + if !b.inuse { + println("runtime:logput fails log entry=", entry, + "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], + "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) + throw("logput: put not legal") + } + b.inuse = false + copy(b.log[1:], b.log[:]) + b.log[0] = entry +} + +func (b *workbuf) checknonempty() { + if b.nobj == 0 { + println("runtime: nonempty check fails", + "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], + "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) + throw("workbuf is empty") + } +} + +func (b *workbuf) checkempty() { + if b.nobj != 0 { + println("runtime: empty check fails", + "b.log[0]=", b.log[0], "b.log[1]=", b.log[1], + "b.log[2]=", b.log[2], "b.log[3]=", b.log[3]) + throw("workbuf is not empty") + } +} + +// checknocurrentwbuf checks that the m's currentwbuf field is empty +func checknocurrentwbuf() { + if getg().m.currentwbuf != 0 { + throw("unexpected currentwbuf") + } +} + +// getempty pops an empty work buffer off the work.empty list, +// allocating new buffers if none are available. +// entry is used to record a brief history of ownership. +//go:nowritebarrier +func getempty(entry uintptr) *workbuf { + var b *workbuf + if work.empty != 0 { + b = (*workbuf)(lfstackpop(&work.empty)) + if b != nil { + b.checkempty() + } + } + if b == nil { + b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), _CacheLineSize, &memstats.gc_sys)) + } + b.logget(entry) + return b +} + +// putempty puts a workbuf onto the work.empty list. +// Upon entry this go routine owns b. The lfstackpush relinquishes ownership. +//go:nowritebarrier +func putempty(b *workbuf, entry uintptr) { + b.checkempty() + b.logput(entry) + lfstackpush(&work.empty, &b.node) +} + +// putfull puts the workbuf on the work.full list for the GC. +// putfull accepts partially full buffers so the GC can avoid competing +// with the mutators for ownership of partially full buffers. +//go:nowritebarrier +func putfull(b *workbuf, entry uintptr) { + b.checknonempty() + b.logput(entry) + lfstackpush(&work.full, &b.node) +} + +// getpartialorempty tries to return a partially empty +// and if none are available returns an empty one. +// entry is used to provide a brief histoy of ownership +// using entry + xxx00000 to +// indicating that two line numbers in the call chain. +//go:nowritebarrier +func getpartialorempty(entry uintptr) *workbuf { + var b *workbuf + // If this m has a buf in currentwbuf then as an optimization + // simply return that buffer. If it turns out currentwbuf + // is full, put it on the work.full queue and get another + // workbuf off the partial or empty queue. + if getg().m.currentwbuf != 0 { + b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0))) + if b != nil { + if b.nobj <= uintptr(len(b.obj)) { + return b + } + putfull(b, entry+80100000) + } + } + b = (*workbuf)(lfstackpop(&work.partial)) + if b != nil { + b.logget(entry) + return b + } + // Let getempty do the logget check but + // use the entry to encode that it passed + // through this routine. + b = getempty(entry + 80700000) + return b +} + +// putpartial puts empty buffers on the work.empty queue, +// full buffers on the work.full queue and +// others on the work.partial queue. +// entry is used to provide a brief histoy of ownership +// using entry + xxx00000 to +// indicating that two call chain line numbers. +//go:nowritebarrier +func putpartial(b *workbuf, entry uintptr) { + if b.nobj == 0 { + putempty(b, entry+81500000) + } else if b.nobj < uintptr(len(b.obj)) { + b.logput(entry) + lfstackpush(&work.partial, &b.node) + } else if b.nobj == uintptr(len(b.obj)) { + b.logput(entry) + lfstackpush(&work.full, &b.node) + } else { + throw("putpartial: bad Workbuf b.nobj") + } +} + +// trygetfull tries to get a full or partially empty workbuffer. +// If one is not immediately available return nil +//go:nowritebarrier +func trygetfull(entry uintptr) *workbuf { + b := (*workbuf)(lfstackpop(&work.full)) + if b == nil { + b = (*workbuf)(lfstackpop(&work.partial)) + } + if b != nil { + b.logget(entry) + b.checknonempty() + return b + } + // full and partial are both empty so see if there + // is an work available on currentwbuf. + // This is an optimization to shift + // processing from the STW marktermination phase into + // the concurrent mark phase. + if getg().m.currentwbuf != 0 { + b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0))) + if b != nil { + if b.nobj != 0 { + return b + } + putempty(b, 839) + b = nil + } + } + return b +} + +// Get a full work buffer off the work.full or a partially +// filled one off the work.partial list. If nothing is available +// wait until all the other gc helpers have finished and then +// return nil. +// getfull acts as a barrier for work.nproc helpers. As long as one +// gchelper is actively marking objects it +// may create a workbuffer that the other helpers can work on. +// The for loop either exits when a work buffer is found +// or when _all_ of the work.nproc GC helpers are in the loop +// looking for work and thus not capable of creating new work. +// This is in fact the termination condition for the STW mark +// phase. +//go:nowritebarrier +func getfull(entry uintptr) *workbuf { + b := (*workbuf)(lfstackpop(&work.full)) + if b != nil { + b.logget(entry) + b.checknonempty() + return b + } + b = (*workbuf)(lfstackpop(&work.partial)) + if b != nil { + b.logget(entry) + return b + } + // Make sure that currentwbuf is also not a source for pointers to be + // processed. This is an optimization that shifts processing + // from the mark termination STW phase to the concurrent mark phase. + if getg().m.currentwbuf != 0 { + b = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0))) + if b != nil { + if b.nobj != 0 { + return b + } + putempty(b, 877) + b = nil + } + } + + xadd(&work.nwait, +1) + for i := 0; ; i++ { + if work.full != 0 { + xadd(&work.nwait, -1) + b = (*workbuf)(lfstackpop(&work.full)) + if b == nil { + b = (*workbuf)(lfstackpop(&work.partial)) + } + if b != nil { + b.logget(entry) + b.checknonempty() + return b + } + xadd(&work.nwait, +1) + } + if work.nwait == work.nproc { + return nil + } + _g_ := getg() + if i < 10 { + _g_.m.gcstats.nprocyield++ + procyield(20) + } else if i < 20 { + _g_.m.gcstats.nosyield++ + osyield() + } else { + _g_.m.gcstats.nsleep++ + usleep(100) + } + } +} + +//go:nowritebarrier +func handoff(b *workbuf) *workbuf { + // Make new buffer with half of b's pointers. + b1 := getempty(915) + n := b.nobj / 2 + b.nobj -= n + b1.nobj = n + memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), n*unsafe.Sizeof(b1.obj[0])) + _g_ := getg() + _g_.m.gcstats.nhandoff++ + _g_.m.gcstats.nhandoffcnt += uint64(n) + + // Put b on full list - let first half of b get stolen. + putfull(b, 942) + return b1 +} + +// 1 when you are harvesting so that the write buffer code shade can +// detect calls during a presumable STW write barrier. +var harvestingwbufs uint32 + +// harvestwbufs moves non-empty workbufs to work.full from m.currentwuf +// Must be in a STW phase. +// xchguintptr is used since there are write barrier calls from the GC helper +// routines even during a STW phase. +// TODO: chase down write barrier calls in STW phase and understand and eliminate +// them. +//go:nowritebarrier +func harvestwbufs() { + // announce to write buffer that you are harvesting the currentwbufs + atomicstore(&harvestingwbufs, 1) + + for mp := allm; mp != nil; mp = mp.alllink { + wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&mp.currentwbuf, 0))) + // TODO: beat write barriers out of the mark termination and eliminate xchg + // tempwbuf := (*workbuf)(unsafe.Pointer(tempm.currentwbuf)) + // tempm.currentwbuf = 0 + if wbuf != nil { + if wbuf.nobj == 0 { + putempty(wbuf, 945) + } else { + putfull(wbuf, 947) //use full instead of partial so GC doesn't compete to get wbuf + } + } + } + + atomicstore(&harvestingwbufs, 0) +} diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go index 058d1c76c4..c7c1198259 100644 --- a/src/runtime/hashmap.go +++ b/src/runtime/hashmap.go @@ -182,8 +182,14 @@ func (h *hmap) createOverflow() { } } -func makemap(t *maptype, hint int64) *hmap { +// makemap implements a Go map creation make(map[k]v, hint) +// If the compiler has determined that the map or the first bucket +// can be created on the stack, h and/or bucket may be non-nil. +// If h != nil, the map can be created directly in h. +// If bucket != nil, bucket can be used as the first bucket. +func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap { if sz := unsafe.Sizeof(hmap{}); sz > 48 || sz != uintptr(t.hmap.size) { + println("runtime: sizeof(hmap) =", sz, ", t.hmap.size =", t.hmap.size) throw("bad hmap size") } @@ -238,7 +244,7 @@ func makemap(t *maptype, hint int64) *hmap { // allocate initial hash table // if B == 0, the buckets field is allocated lazily later (in mapassign) // If hint is large zeroing this memory could take a while. - var buckets unsafe.Pointer + buckets := bucket if B != 0 { if checkgc { memstats.next_gc = memstats.heap_alloc @@ -250,7 +256,9 @@ func makemap(t *maptype, hint int64) *hmap { if checkgc { memstats.next_gc = memstats.heap_alloc } - h := (*hmap)(newobject(t.hmap)) + if h == nil { + h = (*hmap)(newobject(t.hmap)) + } h.count = 0 h.B = B h.flags = 0 @@ -956,7 +964,7 @@ func ismapkey(t *_type) bool { //go:linkname reflect_makemap reflect.makemap func reflect_makemap(t *maptype) *hmap { - return makemap(t, 0) + return makemap(t, 0, nil, nil) } //go:linkname reflect_mapaccess reflect.mapaccess diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 6cbb5f3775..7b4a846195 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -344,7 +344,7 @@ func dumpgoroutine(gp *g) { dumpint(uint64(gp.goid)) dumpint(uint64(gp.gopc)) dumpint(uint64(readgstatus(gp))) - dumpbool(gp.issystem) + dumpbool(isSystemGoroutine(gp)) dumpbool(false) // isbackground dumpint(uint64(gp.waitsince)) dumpstr(gp.waitreason) diff --git a/src/runtime/iface_test.go b/src/runtime/iface_test.go index bca0ea0ee7..bfeb94b8aa 100644 --- a/src/runtime/iface_test.go +++ b/src/runtime/iface_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "runtime" "testing" ) @@ -38,6 +39,47 @@ var ( tl TL ) +// Issue 9370 +func TestCmpIfaceConcreteAlloc(t *testing.T) { + if runtime.Compiler != "gc" { + t.Skip("skipping on non-gc compiler") + } + + n := testing.AllocsPerRun(1, func() { + _ = e == ts + _ = i1 == ts + _ = e == 1 + }) + + if n > 0 { + t.Fatalf("iface cmp allocs=%v; want 0", n) + } +} + +func BenchmarkEqEfaceConcrete(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = e == ts + } +} + +func BenchmarkEqIfaceConcrete(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = i1 == ts + } +} + +func BenchmarkNeEfaceConcrete(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = e != ts + } +} + +func BenchmarkNeIfaceConcrete(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = i1 != ts + } +} + func BenchmarkConvT2ESmall(b *testing.B) { for i := 0; i < b.N; i++ { e = ts diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index b8b1f4ed36..06ba124473 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -312,6 +312,7 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) { // For now this must be bracketed with a stoptheworld and a starttheworld to ensure // all go routines see the new barrier. +//go:nowritebarrier func gcinstallmarkwb() { gcphase = _GCmark } @@ -389,6 +390,7 @@ func gcwork(force int32) { gctimer.cycle.installmarkwb = nanotime() systemstack(stoptheworld) systemstack(gcinstallmarkwb) + systemstack(harvestwbufs) systemstack(starttheworld) gctimer.cycle.mark = nanotime() systemstack(gcmark_m) diff --git a/src/runtime/malloc1.go b/src/runtime/malloc1.go index 4d0754ba9d..18d998b554 100644 --- a/src/runtime/malloc1.go +++ b/src/runtime/malloc1.go @@ -99,7 +99,7 @@ func mallocinit() { var reserved bool // limit = runtime.memlimit(); - // See https://code.google.com/p/go/issues/detail?id=5049 + // See https://golang.org/issue/5049 // TODO(rsc): Fix after 1.1. limit = 0 diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go index 92da2d8209..55f1f82625 100644 --- a/src/runtime/map_test.go +++ b/src/runtime/map_test.go @@ -535,3 +535,13 @@ func benchmarkMapPop(b *testing.B, n int) { func BenchmarkMapPop100(b *testing.B) { benchmarkMapPop(b, 100) } func BenchmarkMapPop1000(b *testing.B) { benchmarkMapPop(b, 1000) } func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) } + +func TestNonEscapingMap(t *testing.T) { + n := testing.AllocsPerRun(1000, func() { + m := make(map[int]int) + m[0] = 0 + }) + if n != 0 { + t.Fatalf("want 0 allocs, got %v", n) + } +} diff --git a/src/runtime/mapspeed_test.go b/src/runtime/mapspeed_test.go index 119eb3f39c..b036d2a3ab 100644 --- a/src/runtime/mapspeed_test.go +++ b/src/runtime/mapspeed_test.go @@ -234,6 +234,15 @@ func BenchmarkNewEmptyMap(b *testing.B) { } } +func BenchmarkNewSmallMap(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + m := make(map[int]int) + m[0] = 0 + m[1] = 1 + } +} + func BenchmarkMapIter(b *testing.B) { m := make(map[int]bool) for i := 0; i < 8; i++ { diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index 33d67c4976..f6e9269858 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -328,6 +328,13 @@ func typedslicecopy(typ *_type, dst, src slice) int { dstp := unsafe.Pointer(dst.array) srcp := unsafe.Pointer(src.array) + if raceenabled { + callerpc := getcallerpc(unsafe.Pointer(&typ)) + pc := funcPC(slicecopy) + racewriterangepc(dstp, uintptr(n)*typ.size, callerpc, pc) + racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc) + } + if !needwb() { memmove(dstp, srcp, uintptr(n)*typ.size) return int(n) diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go index aa99b762bf..920fbcf6d6 100644 --- a/src/runtime/mem_linux.go +++ b/src/runtime/mem_linux.go @@ -54,7 +54,6 @@ func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer { if uintptr(p) < 4096 { if uintptr(p) == _EACCES { print("runtime: mmap: access denied\n") - print("if you're running SELinux, enable execmem for this process.\n") exit(2) } if uintptr(p) == _EAGAIN { diff --git a/src/runtime/mem_plan9.go b/src/runtime/mem_plan9.go index 477a52700e..6ceed25d87 100644 --- a/src/runtime/mem_plan9.go +++ b/src/runtime/mem_plan9.go @@ -48,6 +48,7 @@ func sysFree(v unsafe.Pointer, n uintptr, stat *uint64) { n = memRound(n) if bloc == uintptr(v)+n { bloc -= n + memclr(unsafe.Pointer(bloc), n) } unlock(&memlock) } diff --git a/src/runtime/mem_windows.go b/src/runtime/mem_windows.go index d72d49b975..a800ccae1d 100644 --- a/src/runtime/mem_windows.go +++ b/src/runtime/mem_windows.go @@ -18,16 +18,6 @@ const ( _PAGE_NOACCESS = 0x0001 ) -//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc "kernel32.dll" -//go:cgo_import_dynamic runtime._VirtualFree VirtualFree "kernel32.dll" -//go:cgo_import_dynamic runtime._VirtualProtect VirtualProtect "kernel32.dll" - -var ( - _VirtualAlloc, - _VirtualFree, - _VirtualProtect stdFunction -) - //go:nosplit func sysAlloc(n uintptr, stat *uint64) unsafe.Pointer { xadd64(stat, int64(n)) diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 28afa0dfab..525aa0955a 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -102,7 +102,10 @@ func wakefing() *g { return res } -var fingCreate uint32 +var ( + fingCreate uint32 + fingRunning bool +) func createfing() { // start the finalizer goroutine exactly once @@ -126,9 +129,7 @@ func runfinq() { gp := getg() fing = gp fingwait = true - gp.issystem = true goparkunlock(&finlock, "finalizer wait", traceEvGoBlock) - gp.issystem = false continue } unlock(&finlock) @@ -169,7 +170,9 @@ func runfinq() { default: throw("bad kind in runfinq") } + fingRunning = true reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz)) + fingRunning = false // drop finalizer queue references to finalized object f.fn = nil diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 75b1e52916..1a3e70fcdd 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -122,8 +122,6 @@ const ( _DebugGC = 0 _DebugGCPtrs = false // if true, print trace of every pointer load during GC _ConcurrentSweep = true - - _WorkbufSize = 4 * 256 _FinBlockSize = 4 * 1024 _RootData = 0 _RootBss = 1 @@ -154,12 +152,6 @@ var gcpercent int32 // var worldsema uint32 = 1 -type workbuf struct { - node lfnode // must be first - nobj uintptr - obj [(_WorkbufSize - unsafe.Sizeof(lfnode{}) - ptrSize) / ptrSize]uintptr -} - var data, edata, bss, ebss, gcdata, gcbss struct{} var gcdatamask bitvector @@ -347,7 +339,8 @@ func greyobject(obj, base, off uintptr, hbits heapBits, wbuf *workbuf) *workbuf // If workbuf is full, obtain an empty one. if wbuf.nobj >= uintptr(len(wbuf.obj)) { - wbuf = getempty(wbuf) + putfull(wbuf, 358) + wbuf = getempty(359) } wbuf.obj[wbuf.nobj] = obj @@ -426,13 +419,10 @@ func scanobject(b, n uintptr, ptrmask *uint8, wbuf *workbuf) *workbuf { return wbuf } -// scanblock starts by scanning b as scanobject would. -// If the gcphase is GCscan, that's all scanblock does. -// Otherwise it traverses some fraction of the pointers it found in b, recursively. -// As a special case, scanblock(nil, 0, nil) means to scan previously queued work, -// stopping only when no work is left in the system. +// scanblock scans b as scanobject would. +// If the gcphase is GCscan, scanblock performs additional checks. //go:nowritebarrier -func scanblock(b0, n0 uintptr, ptrmask *uint8) { +func scanblock(b0, n0 uintptr, ptrmask *uint8, wbuf *workbuf) *workbuf { // Use local copies of original parameters, so that a stack trace // due to one of the throws below shows the original block // base and extent. @@ -443,48 +433,40 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8) { // 1. nil - obtain pointer mask from GC bitmap. // 2. pointer to a compact mask (for stacks and data). - wbuf := getpartialorempty() - if b != 0 { - wbuf = scanobject(b, n, ptrmask, wbuf) - if gcphase == _GCscan { - if inheap(b) && ptrmask == nil { - // b is in heap, we are in GCscan so there should be a ptrmask. - throw("scanblock: In GCscan phase and inheap is true.") - } - // GCscan only goes one level deep since mark wb not turned on. - putpartial(wbuf) - return + if wbuf == nil { + wbuf = getpartialorempty(460) // no wbuf passed in. + } + wbuf = scanobject(b, n, ptrmask, wbuf) + if gcphase == _GCscan { + if inheap(b) && ptrmask == nil { + // b is in heap, we are in GCscan so there should be a ptrmask. + throw("scanblock: In GCscan phase and inheap is true.") } } - - drainallwbufs := b == 0 - drainworkbuf(wbuf, drainallwbufs) + return wbuf } -// Scan objects in wbuf until wbuf is empty. -// If drainallwbufs is true find all other available workbufs and repeat the process. +// gcDrain scans objects in work buffers (starting with wbuf), blackening grey +// objects until all work buffers have been drained. //go:nowritebarrier -func drainworkbuf(wbuf *workbuf, drainallwbufs bool) { +func gcDrain(wbuf *workbuf) { + if wbuf == nil { + wbuf = getpartialorempty(472) + } + checknocurrentwbuf() if gcphase != _GCmark && gcphase != _GCmarktermination { - println("gcphase", gcphase) - throw("scanblock phase") + throw("scanblock phase incorrect") } for { if wbuf.nobj == 0 { - if !drainallwbufs { - putempty(wbuf) - return - } + putempty(wbuf, 496) // Refill workbuf from global queue. - wbuf = getfull(wbuf) + wbuf = getfull(504) if wbuf == nil { // nil means out of work barrier reached - return - } - - if wbuf.nobj <= 0 { - throw("runtime:scanblock getfull returns empty buffer") + break } + wbuf.checknonempty() } // If another proc wants a pointer, give it some. @@ -493,52 +475,61 @@ func drainworkbuf(wbuf *workbuf, drainallwbufs bool) { } // This might be a good place to add prefetch code... - // if(wbuf->nobj > 4) { - // PREFETCH(wbuf->obj[wbuf->nobj - 3]; + // if(wbuf.nobj > 4) { + // PREFETCH(wbuf->obj[wbuf.nobj - 3]; // } wbuf.nobj-- b := wbuf.obj[wbuf.nobj] + // If the current wbuf is filled by the scan a new wbuf might be + // returned that could possibly hold only a single object. This + // could result in each iteration draining only a single object + // out of the wbuf passed in + a single object placed + // into an empty wbuf in scanobject so there could be + // a performance hit as we keep fetching fresh wbufs. wbuf = scanobject(b, 0, nil, wbuf) } + checknocurrentwbuf() } -// Scan count objects starting with those in wbuf. +// gcDrainN scans n objects starting with those in wbuf, blackening +// grey objects. //go:nowritebarrier -func drainobjects(wbuf *workbuf, count uintptr) { - for i := uintptr(0); i < count; i++ { +func gcDrainN(wbuf *workbuf, n uintptr) *workbuf { + checknocurrentwbuf() + for i := uintptr(0); i < n; i++ { if wbuf.nobj == 0 { - putempty(wbuf) - wbuf = trygetfull() + putempty(wbuf, 544) + wbuf = trygetfull(545) if wbuf == nil { - return + return nil } } // This might be a good place to add prefetch code... - // if(wbuf->nobj > 4) { - // PREFETCH(wbuf->obj[wbuf->nobj - 3]; + // if(wbuf.nobj > 4) { + // PREFETCH(wbuf->obj[wbuf.nobj - 3]; // } wbuf.nobj-- b := wbuf.obj[wbuf.nobj] wbuf = scanobject(b, 0, nil, wbuf) } - putpartial(wbuf) - return + return wbuf } //go:nowritebarrier func markroot(desc *parfor, i uint32) { // Note: if you add a case here, please also update heapdump.c:dumproots. + wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0))) switch i { case _RootData: - scanblock(uintptr(unsafe.Pointer(&data)), uintptr(unsafe.Pointer(&edata))-uintptr(unsafe.Pointer(&data)), gcdatamask.bytedata) + wbuf = scanblock(uintptr(unsafe.Pointer(&data)), uintptr(unsafe.Pointer(&edata))-uintptr(unsafe.Pointer(&data)), gcdatamask.bytedata, wbuf) case _RootBss: - scanblock(uintptr(unsafe.Pointer(&bss)), uintptr(unsafe.Pointer(&ebss))-uintptr(unsafe.Pointer(&bss)), gcbssmask.bytedata) + wbuf = scanblock(uintptr(unsafe.Pointer(&bss)), uintptr(unsafe.Pointer(&ebss))-uintptr(unsafe.Pointer(&bss)), gcbssmask.bytedata, wbuf) case _RootFinalizers: for fb := allfin; fb != nil; fb = fb.alllink { - scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0]) + wbuf = scanblock(uintptr(unsafe.Pointer(&fb.fin[0])), uintptr(fb.cnt)*unsafe.Sizeof(fb.fin[0]), &finptrmask[0], wbuf) } case _RootSpans: @@ -564,9 +555,9 @@ func markroot(desc *parfor, i uint32) { // A finalizer can be set for an inner byte of an object, find object beginning. p := uintptr(s.start<<_PageShift) + uintptr(spf.special.offset)/s.elemsize*s.elemsize if gcphase != _GCscan { - scanblock(p, s.elemsize, nil) // scanned during mark phase + wbuf = scanblock(p, s.elemsize, nil, wbuf) // scanned during mark phase } - scanblock(uintptr(unsafe.Pointer(&spf.fn)), ptrSize, &oneptr[0]) + wbuf = scanblock(uintptr(unsafe.Pointer(&spf.fn)), ptrSize, &oneptr[0], wbuf) } } @@ -626,157 +617,14 @@ func markroot(desc *parfor, i uint32) { restartg(gp) } } -} - -// Get an empty work buffer off the work.empty list, -// allocating new buffers as needed. -//go:nowritebarrier -func getempty(b *workbuf) *workbuf { - if b != nil { - putfull(b) - b = nil - } - if work.empty != 0 { - b = (*workbuf)(lfstackpop(&work.empty)) - } - if b != nil && b.nobj != 0 { - _g_ := getg() - print("m", _g_.m.id, ": getempty: popped b=", b, " with non-zero b.nobj=", b.nobj, "\n") - throw("getempty: workbuffer not empty, b->nobj not 0") - } - if b == nil { - b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), _CacheLineSize, &memstats.gc_sys)) - b.nobj = 0 - } - return b -} - -//go:nowritebarrier -func putempty(b *workbuf) { - if b.nobj != 0 { - throw("putempty: b->nobj not 0") - } - lfstackpush(&work.empty, &b.node) -} - -//go:nowritebarrier -func putfull(b *workbuf) { - if b.nobj <= 0 { - throw("putfull: b->nobj <= 0") - } - lfstackpush(&work.full, &b.node) -} - -// Get an partially empty work buffer -// if none are available get an empty one. -//go:nowritebarrier -func getpartialorempty() *workbuf { - b := (*workbuf)(lfstackpop(&work.partial)) - if b == nil { - b = getempty(nil) - } - return b -} - -//go:nowritebarrier -func putpartial(b *workbuf) { - if b.nobj == 0 { - lfstackpush(&work.empty, &b.node) - } else if b.nobj < uintptr(len(b.obj)) { - lfstackpush(&work.partial, &b.node) - } else if b.nobj == uintptr(len(b.obj)) { - lfstackpush(&work.full, &b.node) - } else { - print("b=", b, " b.nobj=", b.nobj, " len(b.obj)=", len(b.obj), "\n") - throw("putpartial: bad Workbuf b.nobj") - } -} - -// trygetfull tries to get a full or partially empty workbuffer. -// if one is not immediately available return nil -//go:nowritebarrier -func trygetfull() *workbuf { - wbuf := (*workbuf)(lfstackpop(&work.full)) if wbuf == nil { - wbuf = (*workbuf)(lfstackpop(&work.partial)) - } - return wbuf -} - -// Get a full work buffer off the work.full or a partially -// filled one off the work.partial list. If nothing is available -// wait until all the other gc helpers have finished and then -// return nil. -// getfull acts as a barrier for work.nproc helpers. As long as one -// gchelper is actively marking objects it -// may create a workbuffer that the other helpers can work on. -// The for loop either exits when a work buffer is found -// or when _all_ of the work.nproc GC helpers are in the loop -// looking for work and thus not capable of creating new work. -// This is in fact the termination condition for the STW mark -// phase. -//go:nowritebarrier -func getfull(b *workbuf) *workbuf { - if b != nil { - putempty(b) - } - - b = (*workbuf)(lfstackpop(&work.full)) - if b == nil { - b = (*workbuf)(lfstackpop(&work.partial)) - } - if b != nil { - return b - } - - xadd(&work.nwait, +1) - for i := 0; ; i++ { - if work.full != 0 { - xadd(&work.nwait, -1) - b = (*workbuf)(lfstackpop(&work.full)) - if b == nil { - b = (*workbuf)(lfstackpop(&work.partial)) - } - if b != nil { - return b - } - xadd(&work.nwait, +1) - } - if work.nwait == work.nproc { - return nil - } - _g_ := getg() - if i < 10 { - _g_.m.gcstats.nprocyield++ - procyield(20) - } else if i < 20 { - _g_.m.gcstats.nosyield++ - osyield() - } else { - _g_.m.gcstats.nsleep++ - usleep(100) - } + return + } else { + putpartial(wbuf, 670) } } //go:nowritebarrier -func handoff(b *workbuf) *workbuf { - // Make new buffer with half of b's pointers. - b1 := getempty(nil) - n := b.nobj / 2 - b.nobj -= n - b1.nobj = n - memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), n*unsafe.Sizeof(b1.obj[0])) - _g_ := getg() - _g_.m.gcstats.nhandoff++ - _g_.m.gcstats.nhandoffcnt += uint64(n) - - // Put b on full list - let first half of b get stolen. - lfstackpush(&work.full, &b.node) - return b1 -} - -//go:nowritebarrier func stackmapdata(stkmap *stackmap, n int32) bitvector { if n < 0 || n >= stkmap.n { throw("stackmapdata: index out of range") @@ -786,13 +634,13 @@ func stackmapdata(stkmap *stackmap, n int32) bitvector { // Scan a stack frame: local variables and function arguments/results. //go:nowritebarrier -func scanframe(frame *stkframe, unused unsafe.Pointer) bool { +func scanframeworker(frame *stkframe, unused unsafe.Pointer, wbuf *workbuf) *workbuf { f := frame.fn targetpc := frame.continpc if targetpc == 0 { // Frame is dead. - return true + return wbuf } if _DebugGC > 1 { print("scanframe ", funcname(f), "\n") @@ -831,7 +679,7 @@ func scanframe(frame *stkframe, unused unsafe.Pointer) bool { } bv := stackmapdata(stkmap, pcdata) size = (uintptr(bv.n) / typeBitsWidth) * ptrSize - scanblock(frame.varp-size, size, bv.bytedata) + wbuf = scanblock(frame.varp-size, size, bv.bytedata, wbuf) } // Scan arguments. @@ -852,9 +700,9 @@ func scanframe(frame *stkframe, unused unsafe.Pointer) bool { } bv = stackmapdata(stkmap, pcdata) } - scanblock(frame.argp, uintptr(bv.n)/typeBitsWidth*ptrSize, bv.bytedata) + wbuf = scanblock(frame.argp, uintptr(bv.n)/typeBitsWidth*ptrSize, bv.bytedata, wbuf) } - return true + return wbuf } //go:nowritebarrier @@ -889,8 +737,19 @@ func scanstack(gp *g) { throw("can't scan gchelper stack") } + wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, 0))) + scanframe := func(frame *stkframe, unused unsafe.Pointer) bool { + // Pick up wbuf as free variable so gentraceback and friends can + // keep the same signature. + wbuf = scanframeworker(frame, unused, wbuf) + return true + } gentraceback(^uintptr(0), ^uintptr(0), 0, gp, 0, nil, 0x7fffffff, scanframe, nil, 0) tracebackdefers(gp, scanframe, nil) + wbuf = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, uintptr(unsafe.Pointer(wbuf))))) + if wbuf != nil { + throw("wbuf not nil after stack scans") + } gp.gcscanvalid = true } @@ -898,17 +757,34 @@ func scanstack(gp *g) { // The object is not nil and known to be in the heap. //go:nowritebarrier func shade(b uintptr) { + var wbuf *workbuf + if !inheap(b) { throw("shade: passed an address not in the heap") } - - wbuf := getpartialorempty() - if obj, hbits := heapBitsForObject(b); obj != 0 { - wbuf = greyobject(obj, 0, 0, hbits, wbuf) - } + // TODO: this would be a great place to put a check to see + // if we are harvesting and if we are then we should + // figure out why there is a call to shade when the + // harvester thinks we are in a STW. + // if atomicload(&harvestingwbufs) == uint32(1) { + // // Throw here to discover write barriers + // // being executed during a STW. + // } - putpartial(wbuf) + wbuf = getpartialorempty(1181) + wbuf := greyobject(obj, 0, 0, hbits, wbuf) + checknocurrentwbuf() + // This is part of the write barrier so put the wbuf back. + if gcphase == _GCmarktermination { + putpartial(wbuf, 1191) // Put on full??? + } else { + wbuf = (*workbuf)(unsafe.Pointer(xchguintptr(&getg().m.currentwbuf, uintptr(unsafe.Pointer(wbuf))))) + if wbuf != nil { + throw("m.currentwbuf lost in shade") + } + } + } } // gchelpwork does a small bounded amount of gc work. The purpose is to @@ -931,10 +807,24 @@ func gchelpwork() { // scanstack(gp) case _GCmark: // Get a full work buffer and empty it. - var wbuf *workbuf - wbuf = trygetfull() + m := getg().m + // drain your own currentwbuf first in the hopes that it will + // be more cache friendly. + wbuf := (*workbuf)(unsafe.Pointer(xchguintptr(&m.currentwbuf, 0))) + // wbuf := (*workbuf)(unsafe.Pointer(m.currentwbuf)) + // m.currentwbuf = 0 + if wbuf == nil { + wbuf = trygetfull(1228) + } if wbuf != nil { - drainobjects(wbuf, uintptr(len(wbuf.obj))) // drain upto one buffer's worth of objects + wbuf = gcDrainN(wbuf, uintptr(len(wbuf.obj))) // drain upto one buffer's worth of objects + if wbuf != nil { + if wbuf.nobj != 0 { + putfull(wbuf, 1175) + } else { + putempty(wbuf, 1177) + } + } } case _GCmarktermination: // We should never be here since the world is stopped. @@ -1249,7 +1139,7 @@ func gchelper() { // parallel mark for over GC roots parfordo(work.markfor) if gcphase != _GCscan { - scanblock(0, 0, nil) // blocks in getfull + gcDrain(nil) // blocks in getfull } if trace.enabled { @@ -1380,7 +1270,7 @@ var heapminimum = uint64(4 << 20) func gcinit() { if unsafe.Sizeof(workbuf{}) != _WorkbufSize { - throw("runtime: size of Workbuf is suboptimal") + throw("size of Workbuf is suboptimal") } work.markfor = parforalloc(_MaxGcproc) @@ -1508,16 +1398,12 @@ func gcscan_m() { } // Mark all objects that are known about. +// This is the concurrent mark phase. //go:nowritebarrier func gcmark_m() { - scanblock(0, 0, nil) -} - -// For now this must be bracketed with a stoptheworld and a starttheworld to ensure -// all go routines see the new barrier. -//go:nowritebarrier -func gcinstallmarkwb_m() { - gcphase = _GCmark + gcDrain(nil) + // TODO add another harvestwbuf and reset work.nwait=0, work.ndone=0, and work.nproc=1 + // and repeat the above gcDrain. } // For now this must be bracketed with a stoptheworld and a starttheworld to ensure @@ -1527,12 +1413,14 @@ func gcinstalloffwb_m() { gcphase = _GCoff } +// STW is in effect at this point. //TODO go:nowritebarrier func gc(start_time int64, eagersweep bool) { if _DebugGCPtrs { print("GC start\n") } + gcphase = _GCmarktermination if debug.allocfreetrace > 0 { tracegc() } @@ -1571,12 +1459,10 @@ func gc(start_time int64, eagersweep bool) { mheap_.gcspans = mheap_.allspans work.spans = h_allspans unlock(&mheap_.lock) - oldphase := gcphase work.nwait = 0 work.ndone = 0 work.nproc = uint32(gcprocs()) - gcphase = _GCmarktermination // World is stopped so allglen will not change. for i := uintptr(0); i < allglen; i++ { @@ -1599,9 +1485,10 @@ func gc(start_time int64, eagersweep bool) { t2 = nanotime() } + harvestwbufs() // move local workbufs onto global queues where the GC can find them gchelperstart() parfordo(work.markfor) - scanblock(0, 0, nil) + gcDrain(nil) if work.full != 0 { throw("work.full != 0") @@ -1610,7 +1497,7 @@ func gc(start_time int64, eagersweep bool) { throw("work.partial != 0") } - gcphase = oldphase + gcphase = _GCoff var t3 int64 if debug.gctrace > 0 { t3 = nanotime() @@ -1755,7 +1642,7 @@ func readGCStats_m(pauses *[]uint64) { p := *pauses // Calling code in runtime/debug should make the slice large enough. if cap(p) < len(memstats.pause_ns)+3 { - throw("runtime: short slice passed to readGCStats") + throw("short slice passed to readGCStats") } // Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns. diff --git a/src/runtime/mgc0.go b/src/runtime/mgc0.go index bbd786d519..f54d93377d 100644 --- a/src/runtime/mgc0.go +++ b/src/runtime/mgc0.go @@ -62,7 +62,6 @@ func clearpools() { // bggc holds the state of the backgroundgc. func backgroundgc() { bggc.g = getg() - bggc.g.issystem = true for { gcwork(0) lock(&bggc.lock) @@ -73,7 +72,6 @@ func backgroundgc() { func bgsweep() { sweep.g = getg() - getg().issystem = true for { for gosweepone() != ^uintptr(0) { sweep.nbgsweep++ diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 11bc809ec1..13f1b363d0 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -318,8 +318,7 @@ HaveSpan: t.needzero = s.needzero s.state = _MSpanStack // prevent coalescing with s t.state = _MSpanStack - mHeap_FreeSpanLocked(h, t, false, false) - t.unusedsince = s.unusedsince // preserve age (TODO: wrong: t is possibly merged and/or deallocated at this point) + mHeap_FreeSpanLocked(h, t, false, false, s.unusedsince) s.state = _MSpanFree } s.unusedsince = 0 @@ -395,7 +394,7 @@ func mHeap_Grow(h *mheap, npage uintptr) bool { h_spans[p+s.npages-1] = s atomicstore(&s.sweepgen, h.sweepgen) s.state = _MSpanInUse - mHeap_FreeSpanLocked(h, s, false, true) + mHeap_FreeSpanLocked(h, s, false, true, 0) return true } @@ -442,7 +441,7 @@ func mHeap_Free(h *mheap, s *mspan, acct int32) { memstats.heap_alloc -= uint64(s.npages << _PageShift) memstats.heap_objects-- } - mHeap_FreeSpanLocked(h, s, true, true) + mHeap_FreeSpanLocked(h, s, true, true, 0) if trace.enabled { traceHeapAlloc() } @@ -458,11 +457,11 @@ func mHeap_FreeStack(h *mheap, s *mspan) { s.needzero = 1 lock(&h.lock) memstats.stacks_inuse -= uint64(s.npages << _PageShift) - mHeap_FreeSpanLocked(h, s, true, true) + mHeap_FreeSpanLocked(h, s, true, true, 0) unlock(&h.lock) } -func mHeap_FreeSpanLocked(h *mheap, s *mspan, acctinuse, acctidle bool) { +func mHeap_FreeSpanLocked(h *mheap, s *mspan, acctinuse, acctidle bool, unusedsince int64) { switch s.state { case _MSpanStack: if s.ref != 0 { @@ -488,7 +487,10 @@ func mHeap_FreeSpanLocked(h *mheap, s *mspan, acctinuse, acctidle bool) { // Stamp newly unused spans. The scavenger will use that // info to potentially give back some pages to the OS. - s.unusedsince = nanotime() + s.unusedsince = unusedsince + if unusedsince == 0 { + s.unusedsince = nanotime() + } s.npreleased = 0 // Coalesce with earlier, later spans. diff --git a/src/runtime/netpoll_windows.go b/src/runtime/netpoll_windows.go index 8e0750d607..0861e20f0c 100644 --- a/src/runtime/netpoll_windows.go +++ b/src/runtime/netpoll_windows.go @@ -10,16 +10,6 @@ import ( const _DWORD_MAX = 0xffffffff -//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort "kernel32.dll" -//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus "kernel32.dll" -//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult "ws2_32.dll" - -var ( - _CreateIoCompletionPort, - _GetQueuedCompletionStatus, - _WSAGetOverlappedResult stdFunction -) - const _INVALID_HANDLE_VALUE = ^uintptr(0) // net_op must be the same as beginning of net.operation. Keep these in sync. diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go index 8655c083b2..2de6b09343 100644 --- a/src/runtime/os1_windows.go +++ b/src/runtime/os1_windows.go @@ -11,6 +11,7 @@ import ( //go:cgo_import_dynamic runtime._AddVectoredExceptionHandler AddVectoredExceptionHandler "kernel32.dll" //go:cgo_import_dynamic runtime._CloseHandle CloseHandle "kernel32.dll" //go:cgo_import_dynamic runtime._CreateEventA CreateEventA "kernel32.dll" +//go:cgo_import_dynamic runtime._CreateIoCompletionPort CreateIoCompletionPort "kernel32.dll" //go:cgo_import_dynamic runtime._CreateThread CreateThread "kernel32.dll" //go:cgo_import_dynamic runtime._CreateWaitableTimerA CreateWaitableTimerA "kernel32.dll" //go:cgo_import_dynamic runtime._CryptAcquireContextW CryptAcquireContextW "advapi32.dll" @@ -21,6 +22,7 @@ import ( //go:cgo_import_dynamic runtime._FreeEnvironmentStringsW FreeEnvironmentStringsW "kernel32.dll" //go:cgo_import_dynamic runtime._GetEnvironmentStringsW GetEnvironmentStringsW "kernel32.dll" //go:cgo_import_dynamic runtime._GetProcAddress GetProcAddress "kernel32.dll" +//go:cgo_import_dynamic runtime._GetQueuedCompletionStatus GetQueuedCompletionStatus "kernel32.dll" //go:cgo_import_dynamic runtime._GetStdHandle GetStdHandle "kernel32.dll" //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo "kernel32.dll" //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext "kernel32.dll" @@ -37,14 +39,22 @@ import ( //go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer "kernel32.dll" //go:cgo_import_dynamic runtime._Sleep Sleep "kernel32.dll" //go:cgo_import_dynamic runtime._SuspendThread SuspendThread "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualFree VirtualFree "kernel32.dll" +//go:cgo_import_dynamic runtime._VirtualProtect VirtualProtect "kernel32.dll" +//go:cgo_import_dynamic runtime._WSAGetOverlappedResult WSAGetOverlappedResult "ws2_32.dll" //go:cgo_import_dynamic runtime._WaitForSingleObject WaitForSingleObject "kernel32.dll" //go:cgo_import_dynamic runtime._WriteFile WriteFile "kernel32.dll" //go:cgo_import_dynamic runtime._timeBeginPeriod timeBeginPeriod "winmm.dll" var ( + // Following syscalls are available on every Windows PC. + // All these variables are set by the Windows executable + // loader before the Go program starts. _AddVectoredExceptionHandler, _CloseHandle, _CreateEventA, + _CreateIoCompletionPort, _CreateThread, _CreateWaitableTimerA, _CryptAcquireContextW, @@ -55,6 +65,7 @@ var ( _FreeEnvironmentStringsW, _GetEnvironmentStringsW, _GetProcAddress, + _GetQueuedCompletionStatus, _GetStdHandle, _GetSystemInfo, _GetThreadContext, @@ -71,12 +82,36 @@ var ( _SetWaitableTimer, _Sleep, _SuspendThread, + _VirtualAlloc, + _VirtualFree, + _VirtualProtect, + _WSAGetOverlappedResult, _WaitForSingleObject, _WriteFile, _timeBeginPeriod stdFunction + + // Following syscalls are only available on some Windows PCs. + // We will load syscalls, if available, before using them. + _AddVectoredContinueHandler, + _GetQueuedCompletionStatusEx stdFunction ) -var _GetQueuedCompletionStatusEx stdFunction +func loadOptionalSyscalls() { + var buf [50]byte // large enough for longest string + strtoptr := func(s string) uintptr { + buf[copy(buf[:], s)] = 0 // nil-terminated for OS + return uintptr(noescape(unsafe.Pointer(&buf[0]))) + } + l := stdcall1(_LoadLibraryA, strtoptr("kernel32.dll")) + findfunc := func(name string) stdFunction { + f := stdcall2(_GetProcAddress, l, strtoptr(name)) + return stdFunction(unsafe.Pointer(f)) + } + if l != 0 { + _AddVectoredContinueHandler = findfunc("AddVectoredContinueHandler") + _GetQueuedCompletionStatusEx = findfunc("GetQueuedCompletionStatusEx") + } +} // in sys_windows_386.s and sys_windows_amd64.s func externalthreadhandler() @@ -117,34 +152,24 @@ func disableWER() { stdcall1(_SetErrorMode, uintptr(errormode)|SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX|SEM_NOOPENFILEERRORBOX) } -var ( - kernel32Name = []byte("kernel32.dll\x00") - addVectoredContinueHandlerName = []byte("AddVectoredContinueHandler\x00") - getQueuedCompletionStatusExName = []byte("GetQueuedCompletionStatusEx\x00") -) - func osinit() { setBadSignalMsg() - kernel32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32Name[0]))) + loadOptionalSyscalls() disableWER() externalthreadhandlerp = funcPC(externalthreadhandler) stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp)) - addVectoredContinueHandler := uintptr(0) - if kernel32 != 0 { - addVectoredContinueHandler = stdcall2(_GetProcAddress, kernel32, uintptr(unsafe.Pointer(&addVectoredContinueHandlerName[0]))) - } - if addVectoredContinueHandler == 0 || unsafe.Sizeof(&kernel32) == 4 { + if _AddVectoredContinueHandler == nil || unsafe.Sizeof(&_AddVectoredContinueHandler) == 4 { // use SetUnhandledExceptionFilter for windows-386 or // if VectoredContinueHandler is unavailable. // note: SetUnhandledExceptionFilter handler won't be called, if debugging. stdcall1(_SetUnhandledExceptionFilter, funcPC(lastcontinuetramp)) } else { - stdcall2(stdFunction(unsafe.Pointer(addVectoredContinueHandler)), 1, funcPC(firstcontinuetramp)) - stdcall2(stdFunction(unsafe.Pointer(addVectoredContinueHandler)), 0, funcPC(lastcontinuetramp)) + stdcall2(_AddVectoredContinueHandler, 1, funcPC(firstcontinuetramp)) + stdcall2(_AddVectoredContinueHandler, 0, funcPC(lastcontinuetramp)) } stdcall2(_SetConsoleCtrlHandler, funcPC(ctrlhandler), 1) @@ -158,10 +183,6 @@ func osinit() { // equivalent threads that all do a mix of GUI, IO, computations, etc. // In such context dynamic priority boosting does nothing but harm, so we turn it off. stdcall2(_SetProcessPriorityBoost, currentProcess, 1) - - if kernel32 != 0 { - _GetQueuedCompletionStatusEx = stdFunction(unsafe.Pointer(stdcall2(_GetProcAddress, kernel32, uintptr(unsafe.Pointer(&getQueuedCompletionStatusExName[0]))))) - } } //go:nosplit @@ -506,7 +527,7 @@ func profilem(mp *m) { r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15)) r.contextflags = _CONTEXT_CONTROL stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r))) - dosigprof(r, gp, mp) + sigprof((*byte)(unsafe.Pointer(r.ip())), (*byte)(unsafe.Pointer(r.sp())), nil, gp, mp) } func profileloop1() { diff --git a/src/runtime/os1_windows_386.go b/src/runtime/os1_windows_386.go index 7b4fdfe94a..b7eae204d1 100644 --- a/src/runtime/os1_windows_386.go +++ b/src/runtime/os1_windows_386.go @@ -118,7 +118,3 @@ func sigenable(sig uint32) { func sigdisable(sig uint32) { } - -func dosigprof(r *context, gp *g, mp *m) { - sigprof((*byte)(unsafe.Pointer(uintptr(r.eip))), (*byte)(unsafe.Pointer(uintptr(r.esp))), nil, gp, mp) -} diff --git a/src/runtime/os1_windows_amd64.go b/src/runtime/os1_windows_amd64.go index c211f6fd91..4163fcf23d 100644 --- a/src/runtime/os1_windows_amd64.go +++ b/src/runtime/os1_windows_amd64.go @@ -137,7 +137,3 @@ func sigenable(sig uint32) { func sigdisable(sig uint32) { } - -func dosigprof(r *context, gp *g, mp *m) { - sigprof((*byte)(unsafe.Pointer(uintptr(r.rip))), (*byte)(unsafe.Pointer(uintptr(r.rsp))), nil, gp, mp) -} diff --git a/src/runtime/os3_plan9.go b/src/runtime/os3_plan9.go index 58ca0e7b2f..facaab2546 100644 --- a/src/runtime/os3_plan9.go +++ b/src/runtime/os3_plan9.go @@ -72,9 +72,9 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int { return _NCONT } if flags&_SigNotify != 0 { - // TODO(ality): See if os/signal wants it. - //if(sigsend(...)) - // return _NCONT; + if sendNote(note) { + return _NCONT + } } if flags&_SigKill != 0 { goto Exit diff --git a/src/runtime/os_darwin_arm.go b/src/runtime/os_darwin_arm.go new file mode 100644 index 0000000000..d3336c012a --- /dev/null +++ b/src/runtime/os_darwin_arm.go @@ -0,0 +1,17 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +func checkgoarm() { + return // TODO(minux) +} + +//go:nosplit +func cputicks() int64 { + // Currently cputicks() is used in blocking profiler and to seed runtime·fastrand1(). + // runtime·nanotime() is a poor approximation of CPU ticks that is enough for the profiler. + // TODO: need more entropy to better seed fastrand1. + return nanotime() +} diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go index 679bf34519..870404b948 100644 --- a/src/runtime/os_plan9.go +++ b/src/runtime/os_plan9.go @@ -6,6 +6,11 @@ package runtime import "unsafe" +func close(fd int32) int32 + +//go:noescape +func open(name *byte, mode, perm int32) int32 + //go:noescape func pread(fd int32, buf unsafe.Pointer, nbytes int32, offset int64) int32 diff --git a/src/runtime/os_windows_386.go b/src/runtime/os_windows_386.go deleted file mode 100644 index 86a1906c0c..0000000000 --- a/src/runtime/os_windows_386.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -// contextPC returns the EIP (program counter) register from the context. -func contextPC(r *context) uintptr { return uintptr(r.eip) } - -// contextSP returns the ESP (stack pointer) register from the context. -func contextSP(r *context) uintptr { return uintptr(r.esp) } diff --git a/src/runtime/os_windows_amd64.go b/src/runtime/os_windows_amd64.go deleted file mode 100644 index 3f4d4d07cb..0000000000 --- a/src/runtime/os_windows_amd64.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package runtime - -// contextPC returns the RIP (program counter) register from the context. -func contextPC(r *context) uintptr { return uintptr(r.rip) } - -// contextSP returns the RSP (stack pointer) register from the context. -func contextSP(r *context) uintptr { return uintptr(r.rsp) } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index c8f6de1ac8..027416a9ec 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -32,7 +32,9 @@ func main() { maxstacksize = 250000000 } - systemstack(newsysmon) + systemstack(func() { + newm(sysmon, nil) + }) // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won't care, but a few @@ -110,7 +112,6 @@ func init() { func forcegchelper() { forcegc.g = getg() - forcegc.g.issystem = true for { lock(&forcegc.lock) if forcegc.idle != 0 { diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 70addbffad..471ffc83a3 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -81,10 +81,6 @@ func schedinit() { } } -func newsysmon() { - _newm(sysmon, nil) -} - func dumpgstatus(gp *g) { _g_ := getg() print("runtime: gp: gp=", gp, ", goid=", gp.goid, ", gp->atomicstatus=", readgstatus(gp), "\n") @@ -638,7 +634,7 @@ func starttheworld() { notewakeup(&mp.park) } else { // Start M to run P. Do not start another M below. - _newm(nil, p) + newm(nil, p) add = false } } @@ -658,7 +654,7 @@ func starttheworld() { // coordinate. This lazy approach works out in practice: // we don't mind if the first couple gc rounds don't have quite // the maximum number of procs. - _newm(mhelpgc, nil) + newm(mhelpgc, nil) } _g_.m.locks-- if _g_.m.locks == 0 && _g_.preempt { // restore the preemption request in case we've cleared it in newstack @@ -960,7 +956,7 @@ func unlockextra(mp *m) { } // Create a new m. It will start off with a call to fn, or else the scheduler. -func _newm(fn func(), _p_ *p) { +func newm(fn func(), _p_ *p) { mp := allocm(_p_) mp.nextp = _p_ mp.mstartfn = *(*unsafe.Pointer)(unsafe.Pointer(&fn)) @@ -1037,7 +1033,7 @@ func startm(_p_ *p, spinning bool) { if spinning { fn = mspinning } - _newm(fn, _p_) + newm(fn, _p_) return } if mp.spinning { @@ -2636,7 +2632,7 @@ func checkdead() { lock(&allglock) for i := 0; i < len(allgs); i++ { gp := allgs[i] - if gp.issystem { + if isSystemGoroutine(gp) { continue } s := readgstatus(gp) @@ -2667,7 +2663,7 @@ func checkdead() { } mp := mget() if mp == nil { - _newm(nil, _p_) + newm(nil, _p_) } else { mp.nextp = _p_ notewakeup(&mp.park) diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 3b78b01ca3..88cd48486a 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -435,6 +435,18 @@ func BenchmarkCreateGoroutinesCapture(b *testing.B) { } } +func BenchmarkClosureCall(b *testing.B) { + sum := 0 + off1 := 1 + for i := 0; i < b.N; i++ { + off2 := 2 + func() { + sum += i + off1 + off2 + }() + } + _ = sum +} + type Matrix [][]float64 func BenchmarkMatmult(b *testing.B) { diff --git a/src/runtime/race/race_test.go b/src/runtime/race/race_test.go index 7e0ee866a6..f4caff0ed4 100644 --- a/src/runtime/race/race_test.go +++ b/src/runtime/race/race_test.go @@ -152,7 +152,7 @@ func runTests() ([]byte, error) { } cmd.Env = append(cmd.Env, env) } - cmd.Env = append(cmd.Env, `GORACE="suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0"`) + cmd.Env = append(cmd.Env, `GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0 exitcode=0`) return cmd.CombinedOutput() } diff --git a/src/runtime/race/testdata/slice_test.go b/src/runtime/race/testdata/slice_test.go index 5702d1ac85..32ae878970 100644 --- a/src/runtime/race/testdata/slice_test.go +++ b/src/runtime/race/testdata/slice_test.go @@ -144,6 +144,54 @@ func TestNoRaceSliceCopyRead(t *testing.T) { <-ch } +func TestRacePointerSliceCopyRead(t *testing.T) { + ch := make(chan bool, 1) + a := make([]*int, 10) + b := make([]*int, 10) + go func() { + _ = a[5] + ch <- true + }() + copy(a, b) + <-ch +} + +func TestNoRacePointerSliceWriteCopy(t *testing.T) { + ch := make(chan bool, 1) + a := make([]*int, 10) + b := make([]*int, 10) + go func() { + a[5] = new(int) + ch <- true + }() + copy(a[:5], b[:5]) + <-ch +} + +func TestRacePointerSliceCopyWrite2(t *testing.T) { + ch := make(chan bool, 1) + a := make([]*int, 10) + b := make([]*int, 10) + go func() { + b[5] = new(int) + ch <- true + }() + copy(a, b) + <-ch +} + +func TestNoRacePointerSliceCopyRead(t *testing.T) { + ch := make(chan bool, 1) + a := make([]*int, 10) + b := make([]*int, 10) + go func() { + _ = b[5] + ch <- true + }() + copy(a, b) + <-ch +} + func TestNoRaceSliceWriteSlice2(t *testing.T) { ch := make(chan bool, 1) a := make([]float64, 10) @@ -395,6 +443,53 @@ func TestRaceSliceAppendString(t *testing.T) { <-c } +func TestRacePointerSliceAppend(t *testing.T) { + c := make(chan bool, 1) + s := make([]*int, 10, 20) + go func() { + _ = append(s, new(int)) + c <- true + }() + _ = append(s, new(int)) + <-c +} + +func TestRacePointerSliceAppendWrite(t *testing.T) { + c := make(chan bool, 1) + s := make([]*int, 10) + go func() { + _ = append(s, new(int)) + c <- true + }() + s[0] = new(int) + <-c +} + +func TestRacePointerSliceAppendSlice(t *testing.T) { + c := make(chan bool, 1) + s := make([]*int, 10) + go func() { + s2 := make([]*int, 10) + _ = append(s, s2...) + c <- true + }() + s[0] = new(int) + <-c +} + +func TestRacePointerSliceAppendSlice2(t *testing.T) { + c := make(chan bool, 1) + s := make([]*int, 10) + s2foobar := make([]*int, 10) + go func() { + _ = append(s, s2foobar...) + c <- true + }() + println("WRITE:", &s2foobar[5]) + s2foobar[5] = nil + <-c +} + func TestNoRaceSliceIndexAccess(t *testing.T) { c := make(chan bool, 1) s := make([]int, 10) diff --git a/src/runtime/race_amd64.s b/src/runtime/race_amd64.s index 972cbe3f8a..267cd6cec4 100644 --- a/src/runtime/race_amd64.s +++ b/src/runtime/race_amd64.s @@ -383,8 +383,9 @@ TEXT runtime·racesymbolizethunk(SB), NOSPLIT, $56-8 MOVQ g_m(R13), R13 MOVQ m_g0(R13), R14 MOVQ R14, g(R12) // g = m->g0 - MOVQ RARG0, 0(SP) // func arg + PUSHQ RARG0 // func arg CALL runtime·racesymbolize(SB) + POPQ R12 // All registers are smashed after Go code, reload. get_tls(R12) MOVQ g(R12), R13 diff --git a/src/runtime/rt0_darwin_arm.s b/src/runtime/rt0_darwin_arm.s new file mode 100644 index 0000000000..4d31e3a78a --- /dev/null +++ b/src/runtime/rt0_darwin_arm.s @@ -0,0 +1,18 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT _rt0_arm_darwin(SB),7,$-4 + // prepare arguments for main (_rt0_go) + MOVW (R13), R0 // argc + MOVW $4(R13), R1 // argv + MOVW $main(SB), R4 + B (R4) + +TEXT main(SB),NOSPLIT,$-8 + // save argc and argv onto stack + MOVM.DB.W [R0-R1], (R13) + MOVW $runtime·rt0_go(SB), R4 + B (R4) diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 4a74dd372a..4911dc000d 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -1,21 +1,23 @@ package runtime_test import ( + "fmt" "io/ioutil" "os" "os/exec" "path/filepath" + "runtime" "testing" ) func checkGdbPython(t *testing.T) { - cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-ex", "python import sys; print('golang gdb python support')") + cmd := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", "python import sys; print('go gdb python support')") out, err := cmd.CombinedOutput() if err != nil { - t.Skipf("skipping due to issue running gdb%v", err) + t.Skipf("skipping due to issue running gdb: %v", err) } - if string(out) != "golang gdb python support\n" { + if string(out) != "go gdb python support\n" { t.Skipf("skipping due to lack of python gdb support: %s", out) } } @@ -29,7 +31,6 @@ func main() { ` func TestGdbLoadRuntimeSupport(t *testing.T) { - checkGdbPython(t) dir, err := ioutil.TempDir("", "go-build") @@ -51,7 +52,8 @@ func TestGdbLoadRuntimeSupport(t *testing.T) { t.Fatalf("building source %v\n%s", err, out) } - got, _ := exec.Command("gdb", "-nx", "-q", "--batch", "-ex", "source runtime-gdb.py", + got, _ := exec.Command("gdb", "-nx", "-q", "--batch", "-iex", + fmt.Sprintf("add-auto-load-safe-path %s/src/runtime", runtime.GOROOT()), filepath.Join(dir, "a.exe")).CombinedOutput() if string(got) != "Loading Go Runtime support.\n" { t.Fatalf("%s", got) diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index e38d11a59d..ca3e7d564e 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -199,7 +199,6 @@ type g struct { waitsince int64 // approx time when the g become blocked waitreason string // if status==gwaiting schedlink *g - issystem bool // do not output in stack dump, ignore in deadlock detector preempt bool // preemption signal, duplicates stackguard0 = stackpreempt paniconfault bool // panic (instead of crash) on unexpected fault address preemptscan bool // preempted g does scan for gc @@ -275,6 +274,7 @@ type m struct { waitsemacount uint32 waitsemalock uint32 gcstats gcstats + currentwbuf uintptr // use locks or atomic operations such as xchguinptr to access. needextram bool traceback uint8 waitunlockf unsafe.Pointer // todo go func(*g, unsafe.pointer) bool diff --git a/src/runtime/signal_darwin_arm.go b/src/runtime/signal_darwin_arm.go new file mode 100644 index 0000000000..1441a655ef --- /dev/null +++ b/src/runtime/signal_darwin_arm.go @@ -0,0 +1,44 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import "unsafe" + +type sigctxt struct { + info *siginfo + ctxt unsafe.Pointer +} + +func (c *sigctxt) regs() *regs32 { return &(*ucontext)(c.ctxt).uc_mcontext.ss } +func (c *sigctxt) r0() uint32 { return c.regs().r[0] } +func (c *sigctxt) r1() uint32 { return c.regs().r[1] } +func (c *sigctxt) r2() uint32 { return c.regs().r[2] } +func (c *sigctxt) r3() uint32 { return c.regs().r[3] } +func (c *sigctxt) r4() uint32 { return c.regs().r[4] } +func (c *sigctxt) r5() uint32 { return c.regs().r[5] } +func (c *sigctxt) r6() uint32 { return c.regs().r[6] } +func (c *sigctxt) r7() uint32 { return c.regs().r[7] } +func (c *sigctxt) r8() uint32 { return c.regs().r[8] } +func (c *sigctxt) r9() uint32 { return c.regs().r[9] } +func (c *sigctxt) r10() uint32 { return c.regs().r[10] } +func (c *sigctxt) fp() uint32 { return c.regs().r[11] } +func (c *sigctxt) ip() uint32 { return c.regs().r[12] } +func (c *sigctxt) sp() uint32 { return c.regs().sp } +func (c *sigctxt) lr() uint32 { return c.regs().lr } +func (c *sigctxt) pc() uint32 { return c.regs().pc } +func (c *sigctxt) cpsr() uint32 { return c.regs().cpsr } +func (c *sigctxt) fault() uint32 { return uint32(uintptr(unsafe.Pointer(c.info.si_addr))) } +func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) } +func (c *sigctxt) trap() uint32 { return 0 } +func (c *sigctxt) error() uint32 { return 0 } +func (c *sigctxt) oldmask() uint32 { return 0 } + +func (c *sigctxt) set_pc(x uint32) { c.regs().pc = x } +func (c *sigctxt) set_sp(x uint32) { c.regs().sp = x } +func (c *sigctxt) set_lr(x uint32) { c.regs().lr = x } +func (c *sigctxt) set_r10(x uint32) { c.regs().r[10] = x } + +func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) } +func (c *sigctxt) set_sigaddr(x uint32) { c.info.si_addr = (*byte)(unsafe.Pointer(uintptr(x))) } diff --git a/src/runtime/signals_windows.h b/src/runtime/signals_windows.h deleted file mode 100644 index 6943714b0f..0000000000 --- a/src/runtime/signals_windows.h +++ /dev/null @@ -1,3 +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. diff --git a/src/runtime/sigqueue.go b/src/runtime/sigqueue.go index fbe3425fa6..5cfc926f67 100644 --- a/src/runtime/sigqueue.go +++ b/src/runtime/sigqueue.go @@ -24,6 +24,8 @@ // unnecessary rechecks of sig.mask, but it cannot lead to missed signals // nor deadlocks. +// +build !plan9 + package runtime import "unsafe" diff --git a/src/runtime/sigqueue_plan9.go b/src/runtime/sigqueue_plan9.go new file mode 100644 index 0000000000..b029a300a8 --- /dev/null +++ b/src/runtime/sigqueue_plan9.go @@ -0,0 +1,115 @@ +// 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. + +// This file implements runtime support for signal handling. + +package runtime + +const qsize = 64 + +var sig struct { + q noteQueue + inuse bool + + lock mutex + note note + sleeping bool +} + +type noteQueue struct { + lock mutex + data [qsize]*byte + ri int + wi int + full bool +} + +func (q *noteQueue) push(item *byte) bool { + lock(&q.lock) + if q.full { + unlock(&q.lock) + return false + } + q.data[q.wi] = item + q.wi++ + if q.wi == qsize { + q.wi = 0 + } + if q.wi == q.ri { + q.full = true + } + unlock(&q.lock) + return true +} + +func (q *noteQueue) pop() *byte { + lock(&q.lock) + q.full = false + if q.ri == q.wi { + unlock(&q.lock) + return nil + } + item := q.data[q.ri] + q.ri++ + if q.ri == qsize { + q.ri = 0 + } + unlock(&q.lock) + return item +} + +// Called from sighandler to send a signal back out of the signal handling thread. +// Reports whether the signal was sent. If not, the caller typically crashes the program. +func sendNote(s *byte) bool { + if !sig.inuse { + return false + } + + // Add signal to outgoing queue. + if !sig.q.push(s) { + return false + } + + lock(&sig.lock) + if sig.sleeping { + sig.sleeping = false + notewakeup(&sig.note) + } + unlock(&sig.lock) + + return true +} + +// Called to receive the next queued signal. +// Must only be called from a single goroutine at a time. +func signal_recv() string { + for { + note := sig.q.pop() + if note != nil { + return gostring(note) + } + + lock(&sig.lock) + sig.sleeping = true + noteclear(&sig.note) + unlock(&sig.lock) + notetsleepg(&sig.note, -1) + } +} + +// Must only be called from a single goroutine at a time. +func signal_enable(s uint32) { + if !sig.inuse { + // The first call to signal_enable is for us + // to use for initialization. It does not pass + // signal information in m. + sig.inuse = true // enable reception of signals; cannot disable + noteclear(&sig.note) + return + } +} + +// Must only be called from a single goroutine at a time. +func signal_disable(s uint32) { +} diff --git a/src/runtime/stack.h b/src/runtime/stack.h index b790e70103..88c7e02f40 100644 --- a/src/runtime/stack.h +++ b/src/runtime/stack.h @@ -7,14 +7,22 @@ enum { #ifdef GOOS_windows -#define StackSystem (512*sizeof(uintptr)) -#else +#define STACKSYSTEM (512 * sizeof(uintptr)) +#endif // GOOS_windows #ifdef GOOS_plan9 -#define StackSystem (512) -#else - StackSystem = 0, -#endif // Plan 9 -#endif // Windows +#define STACKSYSTEM 512 +#endif // GOOS_plan9 +#ifdef GOOS_darwin +#ifdef GOARCH_arm +#define STACKSYSTEM 1024 +#endif // GOARCH_arm +#endif // GOOS_darwin + +#ifndef STACKSYSTEM +#define STACKSYSTEM 0 +#endif + + StackSystem = STACKSYSTEM, StackBig = 4096, StackGuard = 640 + StackSystem, diff --git a/src/runtime/stack2.go b/src/runtime/stack2.go index 8a78b1ad96..07a7d38f0c 100644 --- a/src/runtime/stack2.go +++ b/src/runtime/stack2.go @@ -57,9 +57,9 @@ functions to make sure that this limit cannot be violated. const ( // StackSystem is a number of additional bytes to add // to each stack below the usual guard area for OS-specific - // purposes like signal handling. Used on Windows and on - // Plan 9 because they do not use a separate stack. - _StackSystem = goos_windows*512*ptrSize + goos_plan9*512 + // purposes like signal handling. Used on Windows, Plan 9, + // and Darwin/ARM because they do not use a separate stack. + _StackSystem = goos_windows*512*ptrSize + goos_plan9*512 + goos_darwin*goarch_arm*1024 // The minimum size of stack used by Go code _StackMin = 2048 diff --git a/src/runtime/string.go b/src/runtime/string.go index 46c3502f77..0ba309cf02 100644 --- a/src/runtime/string.go +++ b/src/runtime/string.go @@ -129,8 +129,13 @@ func slicebytetostringtmp(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } -func stringtoslicebyte(s string) []byte { - b := rawbyteslice(len(s)) +func stringtoslicebyte(buf *tmpBuf, s string) []byte { + var b []byte + if buf != nil && len(s) <= len(buf) { + b = buf[:len(s)] + } else { + b = rawbyteslice(len(s)) + } copy(b, s) return b } @@ -147,7 +152,7 @@ func stringtoslicebytetmp(s string) []byte { return *(*[]byte)(unsafe.Pointer(&ret)) } -func stringtoslicerune(s string) []rune { +func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune { // two passes. // unlike slicerunetostring, no race because strings are immutable. n := 0 @@ -157,7 +162,12 @@ func stringtoslicerune(s string) []rune { s = s[k:] n++ } - a := rawruneslice(n) + var a []rune + if buf != nil && n <= len(buf) { + a = buf[:n] + } else { + a = rawruneslice(n) + } n = 0 for len(t) > 0 { r, k := charntorune(t) @@ -168,7 +178,7 @@ func stringtoslicerune(s string) []rune { return a } -func slicerunetostring(a []rune) string { +func slicerunetostring(buf *tmpBuf, a []rune) string { if raceenabled && len(a) > 0 { racereadrangepc(unsafe.Pointer(&a[0]), uintptr(len(a))*unsafe.Sizeof(a[0]), @@ -180,7 +190,7 @@ func slicerunetostring(a []rune) string { for _, r := range a { size1 += runetochar(dum[:], r) } - s, b := rawstring(size1 + 3) + s, b := rawstringtmp(buf, size1+3) size2 := 0 for _, r := range a { // check for race @@ -309,11 +319,6 @@ func gobytes(p *byte, n int) []byte { return x } -func gostringsize(n int) string { - s, _ := rawstring(n) - return s -} - func gostring(p *byte) string { l := findnull(p) if l == 0 { diff --git a/src/runtime/stubs3.go b/src/runtime/stubs3.go deleted file mode 100644 index ffaa28775d..0000000000 --- a/src/runtime/stubs3.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build plan9 - -package runtime - -func close(fd int32) int32 - -//go:noescape -func open(name *byte, mode, perm int32) int32 diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index db20ab11e1..3e46d428f7 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -44,8 +44,8 @@ type functab struct { funcoff uintptr } -const minfunc = 16 // minimum function size -const pcbucketsize = 256*minfunc // size of bucket in the pc->func lookup table +const minfunc = 16 // minimum function size +const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table // findfunctab is an array of these structures. // Each bucket represents 4096 bytes of the text segment. @@ -56,7 +56,7 @@ const pcbucketsize = 256*minfunc // size of bucket in the pc->func lookup table // index to find the target function. // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. type findfuncbucket struct { - idx uint32 + idx uint32 subbuckets [16]byte } @@ -154,9 +154,9 @@ func findfunc(pc uintptr) *_func { x := pc - minpc b := x / pcbucketsize - i := x % pcbucketsize / (pcbucketsize/nsub) + i := x % pcbucketsize / (pcbucketsize / nsub) - ffb := (*findfuncbucket)(add(unsafe.Pointer(&findfunctab), b * unsafe.Sizeof(findfuncbucket{}))) + ffb := (*findfuncbucket)(add(unsafe.Pointer(&findfunctab), b*unsafe.Sizeof(findfuncbucket{}))) idx := ffb.idx + uint32(ffb.subbuckets[i]) if pc < ftab[idx].entry { throw("findfunc: bad findfunctab entry") diff --git a/src/runtime/sys_darwin_arm.s b/src/runtime/sys_darwin_arm.s new file mode 100644 index 0000000000..e1b2b664b6 --- /dev/null +++ b/src/runtime/sys_darwin_arm.s @@ -0,0 +1,481 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// System calls and other sys.stuff for ARM, Darwin +// See http://fxr.watson.org/fxr/source/bsd/kern/syscalls.c?v=xnu-1228 +// or /usr/include/sys/syscall.h (on a Mac) for system call numbers. + +#include "go_asm.h" +#include "go_tls.h" +#include "textflag.h" + +// Copied from /usr/include/sys/syscall.h +#define SYS_exit 1 +#define SYS_read 3 +#define SYS_write 4 +#define SYS_open 5 +#define SYS_close 6 +#define SYS_mmap 197 +#define SYS_munmap 73 +#define SYS_madvise 75 +#define SYS_mincore 78 +#define SYS_gettimeofday 116 +#define SYS_kill 37 +#define SYS_getpid 20 +#define SYS___pthread_kill 328 +#define SYS_setitimer 83 +#define SYS___sysctl 202 +#define SYS_sigprocmask 48 +#define SYS_sigaction 46 +#define SYS_sigreturn 184 +#define SYS_select 93 +#define SYS_bsdthread_register 366 +#define SYS_bsdthread_create 360 +#define SYS_bsdthread_terminate 361 +#define SYS_kqueue 362 +#define SYS_kevent 363 +#define SYS_fcntl 92 + +TEXT notok<>(SB),NOSPLIT,$0 + MOVW $0, R8 + MOVW R8, (R8) + B 0(PC) + +TEXT runtime·open(SB),NOSPLIT,$0 + MOVW name+0(FP), R0 + MOVW mode+4(FP), R1 + MOVW perm+8(FP), R2 + MOVW $SYS_open, R12 + SWI $0x80 + MOVW R0, ret+12(FP) + RET + +TEXT runtime·close(SB),NOSPLIT,$0 + MOVW fd+0(FP), R0 + MOVW $SYS_close, R12 + SWI $0x80 + MOVW R0, ret+4(FP) + RET + +TEXT runtime·write(SB),NOSPLIT,$0 + MOVW fd+0(FP), R0 + MOVW p+4(FP), R1 + MOVW n+8(FP), R2 + MOVW $SYS_write, R12 + SWI $0x80 + MOVW R0, ret+12(FP) + RET + +TEXT runtime·read(SB),NOSPLIT,$0 + MOVW fd+0(FP), R0 + MOVW p+4(FP), R1 + MOVW n+8(FP), R2 + MOVW $SYS_read, R12 + SWI $0x80 + MOVW R0, ret+12(FP) + RET + +TEXT runtime·exit(SB),NOSPLIT,$-4 + MOVW 0(FP), R0 + MOVW $SYS_exit, R12 + SWI $0x80 + MOVW $1234, R0 + MOVW $1002, R1 + MOVW R0, (R1) // fail hard + +// Exit this OS thread (like pthread_exit, which eventually +// calls __bsdthread_terminate). +TEXT runtime·exit1(SB),NOSPLIT,$0 + MOVW $SYS_bsdthread_terminate, R12 + SWI $0x80 + MOVW $1234, R0 + MOVW $1003, R1 + MOVW R0, (R1) // fail hard + +TEXT runtime·raise(SB),NOSPLIT,$24 + MOVW $SYS_getpid, R12 + SWI $0x80 + // arg 1 pid already in R0 from getpid + MOVW sig+0(FP), R1 // arg 2 - signal + MOVW $1, R2 // arg 3 - posix + MOVW $SYS_kill, R12 + SWI $0x80 + RET + +TEXT runtime·mmap(SB),NOSPLIT,$0 + MOVW addr+0(FP), R0 + MOVW n+4(FP), R1 + MOVW prot+8(FP), R2 + MOVW flags+12(FP), R3 + MOVW fd+16(FP), R4 + MOVW off+20(FP), R5 + MOVW $0, R6 // off_t is uint64_t + MOVW $SYS_mmap, R12 + SWI $0x80 + MOVW R0, ret+24(FP) + RET + +TEXT runtime·munmap(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW $SYS_munmap, R12 + SWI $0x80 + BL.CS notok<>(SB) + RET + +TEXT runtime·madvise(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_madvise, R12 + SWI $0x80 + BL.CS notok<>(SB) + RET + +TEXT runtime·setitimer(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_setitimer, R12 + SWI $0x80 + RET + +TEXT runtime·mincore(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_mincore, R12 + SWI $0x80 + RET + +TEXT time·now(SB), 7, $32 + MOVW $8(R13), R0 // timeval + MOVW $0, R1 // zone + MOVW $SYS_gettimeofday, R12 + SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec + + MOVW R1, R2 // usec + + MOVW R0, 0(FP) + MOVW $0, R1 + MOVW R1, 4(FP) + MOVW $1000, R3 + MUL R3, R2 + MOVW R2, 8(FP) + RET + +TEXT runtime·nanotime(SB),NOSPLIT,$32 + MOVW $8(R13), R0 // timeval + MOVW $0, R1 // zone + MOVW $SYS_gettimeofday, R12 + SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec + + MOVW R1, R2 + MOVW $1000000000, R3 + MULLU R0, R3, (R1, R0) + MOVW $1000, R3 + MOVW $0, R4 + MUL R3, R2 + ADD.S R2, R0 + ADC R4, R1 + + MOVW R0, 0(FP) + MOVW R1, 4(FP) + RET + +// Sigtramp's job is to call the actual signal handler. +// It is called with the following arguments on the stack: +// LR "return address" - ignored +// R0 actual handler +// R1 siginfo style - ignored +// R2 signal number +// R3 siginfo +// -4(FP) context, beware that 0(FP) is the saved LR +TEXT runtime·sigtramp(SB),NOSPLIT,$0 + // this might be called in external code context, + // where g is not set. + // first save R0, because runtime·load_g will clobber it + MOVM.DB.W [R0], (R13) + MOVB runtime·iscgo(SB), R0 + CMP $0, R0 + BL.NE runtime·load_g(SB) + + CMP $0, g + BNE cont + // fake function call stack frame for badsignal + // we only need to pass R2 (signal number), but + // badsignal will expect R2 at 4(R13), so we also + // push R1 onto stack. turns out we do need R1 + // to do sigreturn. + MOVM.DB.W [R1,R2], (R13) + MOVW $runtime·badsignal(SB), R11 + BL (R11) + MOVM.IA.W [R1], (R13) // saved infostype + ADD $(4+4), R13 // +4: also need to remove the pushed R0. + MOVW -4(FP), R0 // load ucontext + B ret + +cont: + // Restore R0 + MOVM.IA.W (R13), [R0] + + // NOTE: some Darwin/ARM kernels always use the main stack to run the + // signal handler. We need to switch to gsignal ourselves. + MOVW g_m(g), R11 + MOVW m_gsignal(R11), R5 + MOVW (g_stack+stack_hi)(R5), R6 + SUB $28, R6 + + // copy arguments for call to sighandler + MOVW R2, 4(R6) // signal num + MOVW R3, 8(R6) // signal info + MOVW g, 16(R6) // old_g + MOVW -4(FP), R4 + MOVW R4, 12(R6) // context + + // Backup ucontext and infostyle + MOVW R4, 20(R6) + MOVW R1, 24(R6) + + // switch stack and g + MOVW R6, R13 // sigtramp can not re-entrant, so no need to back up R13. + MOVW R5, g + + BL (R0) + + // call sigreturn + MOVW 20(R13), R0 // saved ucontext + MOVW 24(R13), R1 // saved infostyle +ret: + MOVW $SYS_sigreturn, R12 // sigreturn(ucontext, infostyle) + SWI $0x80 + + // if sigreturn fails, we can do nothing but exit + B runtime·exit(SB) + +TEXT runtime·sigprocmask(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_sigprocmask, R12 + SWI $0x80 + BL.CS notok<>(SB) + RET + +TEXT runtime·sigaction(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_sigaction, R12 + SWI $0x80 + RET + +TEXT runtime·usleep(SB),NOSPLIT,$12 + MOVW usec+0(FP), R0 + MOVW R0, R1 + MOVW $1000000, R2 + DIV R2, R0 + MOD R2, R1 + MOVW R0, -12(SP) + MOVW R1, -8(SP) + + // select(0, 0, 0, 0, &tv) + MOVW $0, R0 + MOVW $0, R1 + MOVW $0, R2 + MOVW $0, R3 + MOVW $-12(SP), R4 + MOVW $SYS_select, R12 + SWI $0x80 + RET + +TEXT runtime·cas(SB),NOSPLIT,$0 + B runtime·armcas(SB) + +TEXT runtime·casp1(SB),NOSPLIT,$0 + B runtime·cas(SB) + +TEXT runtime·sysctl(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW 12(FP), R3 + MOVW 16(FP), R4 + MOVW 20(FP), R5 + MOVW $SYS___sysctl, R12 // syscall entry + SWI $0x80 + BCC sysctl_ret + RSB $0, R0, R0 + MOVW R0, ret+24(FP) + RET +sysctl_ret: + MOVW $0, R0 + MOVW R0, ret+24(FP) + RET + +// Thread related functions +// void bsdthread_create(void *stk, M *m, G *g, void (*fn)(void)) +TEXT runtime·bsdthread_create(SB),NOSPLIT,$0 + // Set up arguments to bsdthread_create system call. + // The ones in quotes pass through to the thread callback + // uninterpreted, so we can put whatever we want there. + MOVW fn+12(FP), R0 // "func" + MOVW mm+4(FP), R1 // "arg" + MOVW stk+0(FP), R2 // stack + MOVW gg+8(FP), R3 // "pthread" + MOVW $0x01000000, R4 // flags = PTHREAD_START_CUSTOM + MOVW $0, R5 // paranoia + MOVW $SYS_bsdthread_create, R12 + SWI $0x80 + BCC create_ret + RSB $0, R0, R0 + MOVW R0, ret+16(FP) + RET +create_ret: + MOVW $0, R0 + MOVW R0, ret+16(FP) + RET + +// The thread that bsdthread_create creates starts executing here, +// because we registered this function using bsdthread_register +// at startup. +// R0 = "pthread" +// R1 = mach thread port +// R2 = "func" (= fn) +// R3 = "arg" (= m) +// R4 = stack +// R5 = flags (= 0) +// XXX: how to deal with R4/SP? ref: Libc-594.9.1/arm/pthreads/thread_start.s +TEXT runtime·bsdthread_start(SB),NOSPLIT,$0 + MOVW R1, m_procid(R3) // thread port is m->procid + MOVW m_g0(R3), g + MOVW R3, g_m(g) + // ARM don't have runtime·stackcheck(SB) + // disable runfast mode of vfp + EOR R12, R12 + WORD $0xeee1ca10 // fmxr fpscr, ip + BL (R2) // fn + BL runtime·exit1(SB) + RET + +// int32 bsdthread_register(void) +// registers callbacks for threadstart (see bsdthread_create above +// and wqthread and pthsize (not used). returns 0 on success. +TEXT runtime·bsdthread_register(SB),NOSPLIT,$0 + MOVW $runtime·bsdthread_start(SB), R0 // threadstart + MOVW $0, R1 // wqthread, not used by us + MOVW $0, R2 // pthsize, not used by us + MOVW $0, R3 // dummy_value [sic] + MOVW $0, R4 // targetconc_ptr + MOVW $0, R5 // dispatchqueue_offset + MOVW $SYS_bsdthread_register, R12 // bsdthread_register + SWI $0x80 + MOVW R0, ret+0(FP) + RET + +// uint32 mach_msg_trap(void*, uint32, uint32, uint32, uint32, uint32, uint32) +TEXT runtime·mach_msg_trap(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW 12(FP), R3 + MOVW 16(FP), R4 + MOVW 20(FP), R5 + MOVW 24(FP), R6 + MVN $30, R12 + SWI $0x80 + MOVW R0, 28(FP) + RET + +TEXT runtime·mach_task_self(SB),NOSPLIT,$0 + MVN $27, R12 // task_self_trap + SWI $0x80 + MOVW R0, 0(FP) + RET + +TEXT runtime·mach_thread_self(SB),NOSPLIT,$0 + MVN $26, R12 // thread_self_trap + SWI $0x80 + MOVW R0, 0(FP) + RET + +TEXT runtime·mach_reply_port(SB),NOSPLIT,$0 + MVN $25, R12 // mach_reply_port + SWI $0x80 + MOVW R0, 0(FP) + RET + +// Mach provides trap versions of the semaphore ops, +// instead of requiring the use of RPC. + +// uint32 mach_semaphore_wait(uint32) +TEXT runtime·mach_semaphore_wait(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MVN $35, R12 // semaphore_wait_trap + SWI $0x80 + MOVW R0, ret+4(FP) + RET + +// uint32 mach_semaphore_timedwait(uint32, uint32, uint32) +TEXT runtime·mach_semaphore_timedwait(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MVN $37, R12 // semaphore_timedwait_trap + SWI $0x80 + MOVW R0, ret+12(FP) + RET + +// uint32 mach_semaphore_signal(uint32) +TEXT runtime·mach_semaphore_signal(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MVN $32, R12 // semaphore_signal_trap + SWI $0x80 + MOVW R0, ret+4(FP) + RET + +// uint32 mach_semaphore_signal_all(uint32) +TEXT runtime·mach_semaphore_signal_all(SB),NOSPLIT,$0 + MOVW 0(FP), R0 + MVN $33, R12 // semaphore_signal_all_trap + SWI $0x80 + MOVW R0, ret+4(FP) + RET + +// int32 runtime·kqueue(void) +TEXT runtime·kqueue(SB),NOSPLIT,$0 + MOVW $SYS_kqueue, R12 + SWI $0x80 + RSB.CS $0, R0, R0 + MOVW R0, ret+0(FP) + RET + +// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int events, Timespec *timeout) +TEXT runtime·kevent(SB),NOSPLIT,$0 + MOVW $SYS_kevent, R12 + MOVW kq+0(FP), R0 + MOVW changelist+4(FP), R1 + MOVW nchanges+8(FP), R2 + MOVW eventlist+12(FP), R3 + MOVW nevents+16(FP), R4 + MOVW timeout+20(FP), R5 + SWI $0x80 + RSB.CS $0, R0, R0 + MOVW R0, ret+24(FP) + RET + +// int32 runtime·closeonexec(int32 fd) +TEXT runtime·closeonexec(SB),NOSPLIT,$0 + MOVW $SYS_fcntl, R12 + MOVW 0(FP), R0 + MOVW $2, R1 // F_SETFD + MOVW $1, R2 // FD_CLOEXEC + SWI $0x80 + RET + +// sigaltstack on some darwin/arm version is buggy and will always +// run the signal handler on the main stack, so our sigtramp has +// to do the stack switch ourselves. +TEXT runtime·sigaltstack(SB),NOSPLIT,$0 + RET diff --git a/src/runtime/time.go b/src/runtime/time.go index 50895ca8ec..6a2cc2136c 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -153,7 +153,6 @@ func deltimer(t *timer) bool { // If addtimer inserts a new earlier event, addtimer1 wakes timerproc early. func timerproc() { timers.gp = getg() - timers.gp.issystem = true for { lock(&timers.lock) timers.sleeping = false diff --git a/src/runtime/tls_arm.s b/src/runtime/tls_arm.s index 7c5c0e215e..d130d42cf2 100644 --- a/src/runtime/tls_arm.s +++ b/src/runtime/tls_arm.s @@ -15,8 +15,14 @@ // Note: both functions will clobber R0 and R11 and // can be called from 5c ABI code. -// On android, runtime.tlsg is a normal variable. +// On android and darwin, runtime.tlsg is a normal variable. // TLS offset is computed in x_cgo_inittls. +#ifdef GOOS_android +#define TLSG_IS_VARIABLE +#endif +#ifdef GOOS_darwin +#define TLSG_IS_VARIABLE +#endif // save_g saves the g register into pthread-provided // thread-local memory, so that we can call externally compiled @@ -34,10 +40,11 @@ TEXT runtime·save_g(SB),NOSPLIT,$-4 // a call to runtime.read_tls_fallback which jumps to __kuser_get_tls. // The replacement function saves LR in R11 over the call to read_tls_fallback. MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer + BIC $3, R0 // Darwin/ARM might return unaligned pointer // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. -#ifdef GOOS_android +#ifdef TLSG_IS_VARIABLE MOVW runtime·tlsg(SB), R11 #else MOVW $runtime·tlsg(SB), R11 @@ -57,10 +64,11 @@ TEXT runtime·load_g(SB),NOSPLIT,$0 #endif // See save_g MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer + BIC $3, R0 // Darwin/ARM might return unaligned pointer // $runtime.tlsg(SB) is a special linker symbol. // It is the offset from the TLS base pointer to our // thread-local storage for g. -#ifdef GOOS_android +#ifdef TLSG_IS_VARIABLE MOVW runtime·tlsg(SB), R11 #else MOVW $runtime·tlsg(SB), R11 @@ -68,3 +76,28 @@ TEXT runtime·load_g(SB),NOSPLIT,$0 ADD R11, R0 MOVW 0(R0), g RET + +TEXT runtime·_initcgo(SB),NOSPLIT,$0 +#ifndef GOOS_nacl + // if there is an _cgo_init, call it. + MOVW _cgo_init(SB), R4 + CMP $0, R4 + B.EQ nocgo + MRC 15, 0, R0, C13, C0, 3 // load TLS base pointer + MOVW R0, R3 // arg 3: TLS base pointer + MOVW $runtime·tlsg(SB), R2 // arg 2: tlsg + MOVW $setg_gcc<>(SB), R1 // arg 1: setg + MOVW g, R0 // arg 0: G + BL (R4) // will clobber R0-R3 +#endif +nocgo: + RET + +// void setg_gcc(G*); set g called from gcc. +TEXT setg_gcc<>(SB),NOSPLIT,$0 + MOVW R0, g + B runtime·save_g(SB) + +#ifdef TLSG_IS_VARIABLE +GLOBL runtime·tlsg+0(SB), NOPTR, $4 +#endif diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 6c87d7e2e4..8c31c5abad 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -39,6 +39,11 @@ var ( mstartPC uintptr rt0_goPC uintptr sigpanicPC uintptr + runfinqPC uintptr + backgroundgcPC uintptr + bgsweepPC uintptr + forcegchelperPC uintptr + timerprocPC uintptr systemstack_switchPC uintptr externalthreadhandlerp uintptr // initialized elsewhere @@ -56,6 +61,11 @@ func tracebackinit() { mstartPC = funcPC(mstart) rt0_goPC = funcPC(rt0_go) sigpanicPC = funcPC(sigpanic) + runfinqPC = funcPC(runfinq) + backgroundgcPC = funcPC(backgroundgc) + bgsweepPC = funcPC(bgsweep) + forcegchelperPC = funcPC(forcegchelper) + timerprocPC = funcPC(timerproc) systemstack_switchPC = funcPC(systemstack_switch) } @@ -606,7 +616,7 @@ func tracebackothers(me *g) { lock(&allglock) for _, gp := range allgs { - if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || gp.issystem && level < 2 { + if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 { continue } print("\n") @@ -631,3 +641,14 @@ func topofstack(f *_func) bool { pc == rt0_goPC || externalthreadhandlerp != 0 && pc == externalthreadhandlerp } + +// isSystemGoroutine returns true if the goroutine g must be omitted in +// stack dumps and deadlock detector. +func isSystemGoroutine(gp *g) bool { + pc := gp.startpc + return pc == runfinqPC && !fingRunning || + pc == backgroundgcPC || + pc == bgsweepPC || + pc == forcegchelperPC || + pc == timerprocPC +} diff --git a/src/sort/sort.go b/src/sort/sort.go index b52b54ed8f..0a446c8255 100644 --- a/src/sort/sort.go +++ b/src/sort/sort.go @@ -296,7 +296,7 @@ func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) } // and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40: // The given algorithms are in-place, number of Swap and Assignments // grow as n log n but the algorithm is not stable. -// - "Fast Stable In-Plcae Sorting with O(n) Data Moves" J.I. Munro and +// - "Fast Stable In-Place Sorting with O(n) Data Moves" J.I. Munro and // V. Raman in Algorithmica (1996) 16, 115-160: // This algorithm either needs additional 2n bits or works only if there // are enough different elements available to encode some permutations diff --git a/src/strconv/decimal.go b/src/strconv/decimal.go index 42601283d2..3d7c8d1da9 100644 --- a/src/strconv/decimal.go +++ b/src/strconv/decimal.go @@ -12,7 +12,7 @@ package strconv type decimal struct { - d [800]byte // digits + d [800]byte // digits, big-endian representation nd int // number of digits used dp int // decimal point neg bool @@ -105,7 +105,7 @@ func (a *decimal) Assign(v uint64) { // Signed int has 31 bits, and we have to be able to accommodate 9<<k. const maxShift = 27 -// Binary shift right (* 2) by k bits. k <= maxShift to avoid overflow. +// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow. func rightShift(a *decimal, k uint) { r := 0 // read pointer w := 0 // write pointer @@ -228,7 +228,7 @@ func prefixIsLessThan(b []byte, s string) bool { return false } -// Binary shift left (/ 2) by k bits. k <= maxShift to avoid overflow. +// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow. func leftShift(a *decimal, k uint) { delta := leftcheats[k].delta if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go index 1a9c41b85a..f885d96e9c 100644 --- a/src/strconv/ftoa.go +++ b/src/strconv/ftoa.go @@ -119,7 +119,7 @@ func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte { // Precision for shortest representation mode. switch fmt { case 'e', 'E': - prec = digs.nd - 1 + prec = max(digs.nd-1, 0) case 'f': prec = max(digs.nd-digs.dp, 0) case 'g', 'G': @@ -348,14 +348,13 @@ func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte { if prec > 0 { dst = append(dst, '.') i := 1 - m := d.nd + prec + 1 - max(d.nd, prec+1) - for i < m { - dst = append(dst, d.d[i]) - i++ + m := min(d.nd, prec+1) + if i < m { + dst = append(dst, d.d[i:m]...) + i = m } - for i <= prec { + for ; i <= prec; i++ { dst = append(dst, '0') - i++ } } @@ -373,27 +372,16 @@ func fmtE(dst []byte, neg bool, d decimalSlice, prec int, fmt byte) []byte { } dst = append(dst, ch) - // dddd - var buf [3]byte - i := len(buf) - for exp >= 10 { - i-- - buf[i] = byte(exp%10 + '0') - exp /= 10 + // dd or ddd + switch { + case exp < 10: + dst = append(dst, '0', byte(exp)+'0') + case exp < 100: + dst = append(dst, byte(exp/10)+'0', byte(exp%10)+'0') + default: + dst = append(dst, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0') } - // exp < 10 - i-- - buf[i] = byte(exp + '0') - switch i { - case 0: - dst = append(dst, buf[0], buf[1], buf[2]) - case 1: - dst = append(dst, buf[1], buf[2]) - case 2: - // leading zeroes - dst = append(dst, '0', buf[2]) - } return dst } @@ -406,11 +394,9 @@ func fmtF(dst []byte, neg bool, d decimalSlice, prec int) []byte { // integer, padded with zeros as needed. if d.dp > 0 { - var i int - for i = 0; i < d.dp && i < d.nd; i++ { - dst = append(dst, d.d[i]) - } - for ; i < d.dp; i++ { + m := min(d.nd, d.dp) + dst = append(dst, d.d[:m]...) + for ; m < d.dp; m++ { dst = append(dst, '0') } } else { @@ -467,6 +453,13 @@ func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte { return append(dst, buf[w:]...) } +func min(a, b int) int { + if a < b { + return a + } + return b +} + func max(a, b int) int { if a > b { return a diff --git a/src/sync/atomic/asm_darwin_arm.s b/src/sync/atomic/asm_darwin_arm.s new file mode 100644 index 0000000000..36dd4835f7 --- /dev/null +++ b/src/sync/atomic/asm_darwin_arm.s @@ -0,0 +1,99 @@ +// 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 "textflag.h" + +// Darwin/ARM atomic operations. + +TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0 + B ·armCompareAndSwapUint32(SB) + +TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0 + B ·CompareAndSwapUint32(SB) + +TEXT ·AddInt32(SB),NOSPLIT,$0 + B ·AddUint32(SB) + +TEXT ·AddUint32(SB),NOSPLIT,$0 + B ·armAddUint32(SB) + +TEXT ·AddUintptr(SB),NOSPLIT,$0 + B ·AddUint32(SB) + +TEXT ·SwapInt32(SB),NOSPLIT,$0 + B ·SwapUint32(SB) + +TEXT ·SwapUint32(SB),NOSPLIT,$0 + B ·armSwapUint32(SB) + +TEXT ·SwapUintptr(SB),NOSPLIT,$0 + B ·SwapUint32(SB) + +TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0 + B ·CompareAndSwapUint64(SB) + +TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4 + B ·armCompareAndSwapUint64(SB) + +TEXT ·AddInt64(SB),NOSPLIT,$0 + B ·addUint64(SB) + +TEXT ·AddUint64(SB),NOSPLIT,$0 + B ·addUint64(SB) + +TEXT ·SwapInt64(SB),NOSPLIT,$0 + B ·swapUint64(SB) + +TEXT ·SwapUint64(SB),NOSPLIT,$0 + B ·swapUint64(SB) + +TEXT ·LoadInt32(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·LoadUint32(SB),NOSPLIT,$0-8 + MOVW addr+0(FP), R1 +load32loop: + LDREX (R1), R2 // loads R2 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE load32loop + MOVW R2, val+4(FP) + RET + +TEXT ·LoadInt64(SB),NOSPLIT,$0 + B ·loadUint64(SB) + +TEXT ·LoadUint64(SB),NOSPLIT,$0 + B ·loadUint64(SB) + +TEXT ·LoadUintptr(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·LoadPointer(SB),NOSPLIT,$0 + B ·LoadUint32(SB) + +TEXT ·StoreInt32(SB),NOSPLIT,$0 + B ·StoreUint32(SB) + +TEXT ·StoreUint32(SB),NOSPLIT,$0-8 + MOVW addr+0(FP), R1 + MOVW val+4(FP), R2 +storeloop: + LDREX (R1), R4 // loads R4 + STREX R2, (R1), R0 // stores R2 + CMP $0, R0 + BNE storeloop + RET + +TEXT ·StoreInt64(SB),NOSPLIT,$0 + B ·storeUint64(SB) + +TEXT ·StoreUint64(SB),NOSPLIT,$0 + B ·storeUint64(SB) + +TEXT ·StoreUintptr(SB),NOSPLIT,$0 + B ·StoreUint32(SB) diff --git a/src/syscall/asm_darwin_arm.s b/src/syscall/asm_darwin_arm.s new file mode 100644 index 0000000000..f75aa19086 --- /dev/null +++ b/src/syscall/asm_darwin_arm.s @@ -0,0 +1,134 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// +// System call support for ARM, Darwin +// + +// func Syscall(syscall uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) +TEXT ·Syscall(SB),NOSPLIT,$0-32 + BL runtime·entersyscall(SB) + MOVW 4(SP), R12 + MOVW 8(SP), R0 + MOVW 12(SP), R1 + MOVW 16(SP), R2 + SWI $0x80 + BCC ok + MOVW $-1, R1 + MOVW R1, 20(SP) // r1 + MOVW $0, R2 + MOVW R2, 24(SP) // r2 + MOVW R0, 28(SP) // errno + BL runtime·exitsyscall(SB) + RET +ok: + MOVW R0, 20(SP) // r1 + MOVW R1, 24(SP) // r2 + MOVW $0, R0 + MOVW R0, 28(SP) // errno + BL runtime·exitsyscall(SB) + RET + +// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr) +TEXT ·RawSyscall(SB),NOSPLIT,$0-32 + MOVW 4(SP), R12 // syscall entry + MOVW 8(SP), R0 + MOVW 12(SP), R1 + MOVW 16(SP), R2 + SWI $0x80 + BCC ok1 + MOVW $-1, R1 + MOVW R1, 20(SP) // r1 + MOVW $0, R2 + MOVW R2, 24(SP) // r2 + MOVW R0, 28(SP) // errno + RET +ok1: + MOVW R0, 20(SP) // r1 + MOVW R1, 24(SP) // r2 + MOVW $0, R0 + MOVW R0, 28(SP) // errno + RET + +// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +TEXT ·Syscall6(SB),NOSPLIT,$0-44 + BL runtime·entersyscall(SB) + MOVW 4(SP), R12 // syscall entry + MOVW 8(SP), R0 + MOVW 12(SP), R1 + MOVW 16(SP), R2 + MOVW 20(SP), R3 + MOVW 24(SP), R4 + MOVW 28(SP), R5 + SWI $0x80 + BCC ok6 + MOVW $-1, R1 + MOVW R1, 32(SP) // r1 + MOVW $0, R2 + MOVW R2, 36(SP) // r2 + MOVW R0, 40(SP) // errno + BL runtime·exitsyscall(SB) + RET +ok6: + MOVW R0, 32(SP) // r1 + MOVW R1, 36(SP) // r2 + MOVW $0, R0 + MOVW R0, 40(SP) // errno + BL runtime·exitsyscall(SB) + RET + +// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) +TEXT ·RawSyscall6(SB),NOSPLIT,$0-44 + MOVW 4(SP), R12 // syscall entry + MOVW 8(SP), R0 + MOVW 12(SP), R1 + MOVW 16(SP), R2 + MOVW 20(SP), R3 + MOVW 24(SP), R4 + MOVW 28(SP), R5 + SWI $0x80 + BCC ok2 + MOVW $-1, R1 + MOVW R1, 32(SP) // r1 + MOVW $0, R2 + MOVW R2, 36(SP) // r2 + MOVW R0, 40(SP) // errno + RET +ok2: + MOVW R0, 32(SP) // r1 + MOVW R1, 36(SP) // r2 + MOVW $0, R0 + MOVW R0, 40(SP) // errno + RET + +// Actually Syscall7. +TEXT ·Syscall9(SB),NOSPLIT,$0-56 + BL runtime·entersyscall(SB) + MOVW 4(SP), R12 // syscall entry + MOVW 8(SP), R0 + MOVW 12(SP), R1 + MOVW 16(SP), R2 + MOVW 20(SP), R3 + MOVW 24(SP), R4 + MOVW 28(SP), R5 + MOVW 32(SP), R6 + SWI $0x80 + BCC ok9 + MOVW $-1, R1 + MOVW R1, 44(SP) // r1 + MOVW $0, R2 + MOVW R2, 48(SP) // r2 + MOVW R0, 52(SP) // errno + BL runtime·exitsyscall(SB) + RET +ok9: + MOVW R0, 44(SP) // r1 + MOVW R1, 48(SP) // r2 + MOVW $0, R0 + MOVW R0, 52(SP) // errno + BL runtime·exitsyscall(SB) + RET + diff --git a/src/syscall/syscall_bsd.go b/src/syscall/syscall_bsd.go index 2556fa8746..af563910b1 100644 --- a/src/syscall/syscall_bsd.go +++ b/src/syscall/syscall_bsd.go @@ -68,40 +68,7 @@ func ReadDirent(fd int, buf []byte) (n int, err error) { // actual system call is getdirentries64, 64 is a good guess. // TODO(rsc): Can we use a single global basep for all calls? var base = (*uintptr)(unsafe.Pointer(new(uint64))) - n, err = Getdirentries(fd, buf, base) - - // On OS X 10.10 Yosemite, if you have a directory that can be returned - // in a single getdirentries64 call (for example, a directory with one file), - // and you read from the directory at EOF twice, you get EOF both times: - // fd = open("dir") - // getdirentries64(fd) // returns data - // getdirentries64(fd) // returns 0 (EOF) - // getdirentries64(fd) // returns 0 (EOF) - // - // But if you remove the file in the middle between the two calls, the - // second call returns an error instead. - // fd = open("dir") - // getdirentries64(fd) // returns data - // getdirentries64(fd) // returns 0 (EOF) - // remove("dir/file") - // getdirentries64(fd) // returns ENOENT/EINVAL - // - // Whether you get ENOENT or EINVAL depends on exactly what was - // in the directory. It is deterministic, just data-dependent. - // - // This only happens in small directories. A directory containing more data - // than fits in a 4k getdirentries64 call will return EOF correctly. - // (It's not clear if the criteria is that the directory be split across multiple - // getdirentries64 calls or that it be split across multiple file system blocks.) - // - // We could change package os to avoid the second read at EOF, - // and maybe we should, but that's a bit involved. - // For now, treat the EINVAL/ENOENT as EOF. - if runtime.GOOS == "darwin" && (err == EINVAL || err == ENOENT) { - err = nil - } - - return + return Getdirentries(fd, buf, base) } // Wait status is 7 bits at bottom, either 0 (exited), diff --git a/src/syscall/syscall_darwin_arm.go b/src/syscall/syscall_darwin_arm.go new file mode 100644 index 0000000000..2a7d4f2db6 --- /dev/null +++ b/src/syscall/syscall_darwin_arm.go @@ -0,0 +1,70 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall + +import "unsafe" + +func Getpagesize() int { return 4096 } + +func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) } + +func NsecToTimespec(nsec int64) (ts Timespec) { + ts.Sec = int32(nsec / 1e9) + ts.Nsec = int32(nsec % 1e9) + return +} + +func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 } + +func NsecToTimeval(nsec int64) (tv Timeval) { + nsec += 999 // round up to microsecond + tv.Usec = int32(nsec % 1e9 / 1e3) + tv.Sec = int32(nsec / 1e9) + return +} + +//sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error) +func Gettimeofday(tv *Timeval) (err error) { + // The tv passed to gettimeofday must be non-nil + // but is otherwise unused. The answers come back + // in the two registers. + sec, usec, err := gettimeofday(tv) + tv.Sec = int32(sec) + tv.Usec = int32(usec) + return err +} + +func SetKevent(k *Kevent_t, fd, mode, flags int) { + k.Ident = uint32(fd) + k.Filter = int16(mode) + k.Flags = uint16(flags) +} + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint32(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} + +func sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) { + var length = uint64(count) + + _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr(*offset>>32), uintptr(unsafe.Pointer(&length)), 0, 0, 0, 0) + + written = int(length) + + if e1 != 0 { + err = e1 + } + return +} + +func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno) // sic diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 8ac498df78..feb329f530 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -1003,13 +1003,22 @@ func Readlink(path string, buf []byte) (n int, err error) { } rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0])) - if uintptr(bytesReturned) < unsafe.Sizeof(*rdb) || - rdb.ReparseTag != IO_REPARSE_TAG_SYMLINK { - // the path is not a symlink but another type of reparse point + var s string + switch rdb.ReparseTag { + case IO_REPARSE_TAG_SYMLINK: + data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) + p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) + s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2]) + case _IO_REPARSE_TAG_MOUNT_POINT: + data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) + p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) + s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2]) + default: + // the path is not a symlink or junction but another type of reparse + // point return -1, ENOENT } - - s := UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rdb.PathBuffer[0]))[:rdb.PrintNameLength/2]) n = copy(buf, []byte(s)) + return n, nil } diff --git a/src/syscall/zerrors_darwin_arm.go b/src/syscall/zerrors_darwin_arm.go new file mode 100644 index 0000000000..7e800d4259 --- /dev/null +++ b/src/syscall/zerrors_darwin_arm.go @@ -0,0 +1,1349 @@ +// mkerrors.sh +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs -- _const.go + +package syscall + +const ( + AF_APPLETALK = 0x10 + AF_CCITT = 0xa + AF_CHAOS = 0x5 + AF_CNT = 0x15 + AF_COIP = 0x14 + AF_DATAKIT = 0x9 + AF_DECnet = 0xc + AF_DLI = 0xd + AF_E164 = 0x1c + AF_ECMA = 0x8 + AF_HYLINK = 0xf + AF_IMPLINK = 0x3 + AF_INET = 0x2 + AF_INET6 = 0x1e + AF_IPX = 0x17 + AF_ISDN = 0x1c + AF_ISO = 0x7 + AF_LAT = 0xe + AF_LINK = 0x12 + AF_LOCAL = 0x1 + AF_MAX = 0x25 + AF_NATM = 0x1f + AF_NDRV = 0x1b + AF_NETBIOS = 0x21 + AF_NS = 0x6 + AF_OSI = 0x7 + AF_PPP = 0x22 + AF_PUP = 0x4 + AF_RESERVED_36 = 0x24 + AF_ROUTE = 0x11 + AF_SIP = 0x18 + AF_SNA = 0xb + AF_SYSTEM = 0x20 + AF_UNIX = 0x1 + AF_UNSPEC = 0x0 + B0 = 0x0 + B110 = 0x6e + B115200 = 0x1c200 + B1200 = 0x4b0 + B134 = 0x86 + B14400 = 0x3840 + B150 = 0x96 + B1800 = 0x708 + B19200 = 0x4b00 + B200 = 0xc8 + B230400 = 0x38400 + B2400 = 0x960 + B28800 = 0x7080 + B300 = 0x12c + B38400 = 0x9600 + B4800 = 0x12c0 + B50 = 0x32 + B57600 = 0xe100 + B600 = 0x258 + B7200 = 0x1c20 + B75 = 0x4b + B76800 = 0x12c00 + B9600 = 0x2580 + BIOCFLUSH = 0x20004268 + BIOCGBLEN = 0x40044266 + BIOCGDLT = 0x4004426a + BIOCGDLTLIST = 0xc00c4279 + BIOCGETIF = 0x4020426b + BIOCGHDRCMPLT = 0x40044274 + BIOCGRSIG = 0x40044272 + BIOCGRTIMEOUT = 0x4008426e + BIOCGSEESENT = 0x40044276 + BIOCGSTATS = 0x4008426f + BIOCIMMEDIATE = 0x80044270 + BIOCPROMISC = 0x20004269 + BIOCSBLEN = 0xc0044266 + BIOCSDLT = 0x80044278 + BIOCSETF = 0x80084267 + BIOCSETIF = 0x8020426c + BIOCSHDRCMPLT = 0x80044275 + BIOCSRSIG = 0x80044273 + BIOCSRTIMEOUT = 0x8008426d + BIOCSSEESENT = 0x80044277 + BIOCVERSION = 0x40044271 + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0x0 + BPF_ALIGNMENT = 0x4 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0x0 + BPF_IND = 0x40 + BPF_JA = 0x0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0x0 + BPF_LD = 0x0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXBUFSIZE = 0x80000 + BPF_MAXINSNS = 0x200 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINBUFSIZE = 0x20 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RELEASE = 0x30bb6 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0x0 + BPF_TXA = 0x80 + BPF_W = 0x0 + BPF_X = 0x8 + BRKINT = 0x2 + CFLUSH = 0xf + CLOCAL = 0x8000 + CREAD = 0x800 + CS5 = 0x0 + CS6 = 0x100 + CS7 = 0x200 + CS8 = 0x300 + CSIZE = 0x300 + CSTART = 0x11 + CSTATUS = 0x14 + CSTOP = 0x13 + CSTOPB = 0x400 + CSUSP = 0x1a + CTL_MAXNAME = 0xc + CTL_NET = 0x4 + DLT_APPLE_IP_OVER_IEEE1394 = 0x8a + DLT_ARCNET = 0x7 + DLT_ATM_CLIP = 0x13 + DLT_ATM_RFC1483 = 0xb + DLT_AX25 = 0x3 + DLT_CHAOS = 0x5 + DLT_CHDLC = 0x68 + DLT_C_HDLC = 0x68 + DLT_EN10MB = 0x1 + DLT_EN3MB = 0x2 + DLT_FDDI = 0xa + DLT_IEEE802 = 0x6 + DLT_IEEE802_11 = 0x69 + DLT_IEEE802_11_RADIO = 0x7f + DLT_IEEE802_11_RADIO_AVS = 0xa3 + DLT_LINUX_SLL = 0x71 + DLT_LOOP = 0x6c + DLT_NULL = 0x0 + DLT_PFLOG = 0x75 + DLT_PFSYNC = 0x12 + DLT_PPP = 0x9 + DLT_PPP_BSDOS = 0x10 + DLT_PPP_SERIAL = 0x32 + DLT_PRONET = 0x4 + DLT_RAW = 0xc + DLT_SLIP = 0x8 + DLT_SLIP_BSDOS = 0xf + DT_BLK = 0x6 + DT_CHR = 0x2 + DT_DIR = 0x4 + DT_FIFO = 0x1 + DT_LNK = 0xa + DT_REG = 0x8 + DT_SOCK = 0xc + DT_UNKNOWN = 0x0 + DT_WHT = 0xe + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 + EVFILT_AIO = -0x3 + EVFILT_FS = -0x9 + EVFILT_MACHPORT = -0x8 + EVFILT_PROC = -0x5 + EVFILT_READ = -0x1 + EVFILT_SIGNAL = -0x6 + EVFILT_SYSCOUNT = 0x9 + EVFILT_THREADMARKER = 0x9 + EVFILT_TIMER = -0x7 + EVFILT_VNODE = -0x4 + EVFILT_WRITE = -0x2 + EV_ADD = 0x1 + EV_CLEAR = 0x20 + EV_DELETE = 0x2 + EV_DISABLE = 0x8 + EV_ENABLE = 0x4 + EV_EOF = 0x8000 + EV_ERROR = 0x4000 + EV_FLAG0 = 0x1000 + EV_FLAG1 = 0x2000 + EV_ONESHOT = 0x10 + EV_OOBAND = 0x2000 + EV_POLL = 0x1000 + EV_RECEIPT = 0x40 + EV_SYSFLAGS = 0xf000 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 + FD_CLOEXEC = 0x1 + FD_SETSIZE = 0x400 + FLUSHO = 0x800000 + F_ADDSIGS = 0x3b + F_ALLOCATEALL = 0x4 + F_ALLOCATECONTIG = 0x2 + F_CHKCLEAN = 0x29 + F_DUPFD = 0x0 + F_DUPFD_CLOEXEC = 0x43 + F_FREEZE_FS = 0x35 + F_FULLFSYNC = 0x33 + F_GETFD = 0x1 + F_GETFL = 0x3 + F_GETLK = 0x7 + F_GETOWN = 0x5 + F_GETPATH = 0x32 + F_GLOBAL_NOCACHE = 0x37 + F_LOG2PHYS = 0x31 + F_MARKDEPENDENCY = 0x3c + F_NOCACHE = 0x30 + F_OK = 0x0 + F_PATHPKG_CHECK = 0x34 + F_PEOFPOSMODE = 0x3 + F_PREALLOCATE = 0x2a + F_RDADVISE = 0x2c + F_RDAHEAD = 0x2d + F_RDLCK = 0x1 + F_READBOOTSTRAP = 0x2e + F_SETFD = 0x2 + F_SETFL = 0x4 + F_SETLK = 0x8 + F_SETLKW = 0x9 + F_SETOWN = 0x6 + F_SETSIZE = 0x2b + F_THAW_FS = 0x36 + F_UNLCK = 0x2 + F_VOLPOSMODE = 0x4 + F_WRITEBOOTSTRAP = 0x2f + F_WRLCK = 0x3 + HUPCL = 0x4000 + ICANON = 0x100 + ICRNL = 0x100 + IEXTEN = 0x400 + IFF_ALLMULTI = 0x200 + IFF_ALTPHYS = 0x4000 + IFF_BROADCAST = 0x2 + IFF_DEBUG = 0x4 + IFF_LINK0 = 0x1000 + IFF_LINK1 = 0x2000 + IFF_LINK2 = 0x4000 + IFF_LOOPBACK = 0x8 + IFF_MULTICAST = 0x8000 + IFF_NOARP = 0x80 + IFF_NOTRAILERS = 0x20 + IFF_OACTIVE = 0x400 + IFF_POINTOPOINT = 0x10 + IFF_PROMISC = 0x100 + IFF_RUNNING = 0x40 + IFF_SIMPLEX = 0x800 + IFF_UP = 0x1 + IFNAMSIZ = 0x10 + IFT_1822 = 0x2 + IFT_AAL5 = 0x31 + IFT_ARCNET = 0x23 + IFT_ARCNETPLUS = 0x24 + IFT_ATM = 0x25 + IFT_BRIDGE = 0xd1 + IFT_CARP = 0xf8 + IFT_CEPT = 0x13 + IFT_DS3 = 0x1e + IFT_ENC = 0xf4 + IFT_EON = 0x19 + IFT_ETHER = 0x6 + IFT_FAITH = 0x38 + IFT_FDDI = 0xf + IFT_FRELAY = 0x20 + IFT_FRELAYDCE = 0x2c + IFT_GIF = 0x37 + IFT_HDH1822 = 0x3 + IFT_HIPPI = 0x2f + IFT_HSSI = 0x2e + IFT_HY = 0xe + IFT_IEEE1394 = 0x90 + IFT_IEEE8023ADLAG = 0x88 + IFT_ISDNBASIC = 0x14 + IFT_ISDNPRIMARY = 0x15 + IFT_ISO88022LLC = 0x29 + IFT_ISO88023 = 0x7 + IFT_ISO88024 = 0x8 + IFT_ISO88025 = 0x9 + IFT_ISO88026 = 0xa + IFT_L2VLAN = 0x87 + IFT_LAPB = 0x10 + IFT_LOCALTALK = 0x2a + IFT_LOOP = 0x18 + IFT_MIOX25 = 0x26 + IFT_MODEM = 0x30 + IFT_NSIP = 0x1b + IFT_OTHER = 0x1 + IFT_P10 = 0xc + IFT_P80 = 0xd + IFT_PARA = 0x22 + IFT_PDP = 0xff + IFT_PFLOG = 0xf5 + IFT_PFSYNC = 0xf6 + IFT_PPP = 0x17 + IFT_PROPMUX = 0x36 + IFT_PROPVIRTUAL = 0x35 + IFT_PTPSERIAL = 0x16 + IFT_RS232 = 0x21 + IFT_SDLC = 0x11 + IFT_SIP = 0x1f + IFT_SLIP = 0x1c + IFT_SMDSDXI = 0x2b + IFT_SMDSICIP = 0x34 + IFT_SONET = 0x27 + IFT_SONETPATH = 0x32 + IFT_SONETVT = 0x33 + IFT_STARLAN = 0xb + IFT_STF = 0x39 + IFT_T1 = 0x12 + IFT_ULTRA = 0x1d + IFT_V35 = 0x2d + IFT_X25 = 0x5 + IFT_X25DDN = 0x4 + IFT_X25PLE = 0x28 + IFT_XETHER = 0x1a + IGNBRK = 0x1 + IGNCR = 0x80 + IGNPAR = 0x4 + IMAXBEL = 0x2000 + INLCR = 0x40 + INPCK = 0x10 + IN_CLASSA_HOST = 0xffffff + IN_CLASSA_MAX = 0x80 + IN_CLASSA_NET = 0xff000000 + IN_CLASSA_NSHIFT = 0x18 + IN_CLASSB_HOST = 0xffff + IN_CLASSB_MAX = 0x10000 + IN_CLASSB_NET = 0xffff0000 + IN_CLASSB_NSHIFT = 0x10 + IN_CLASSC_HOST = 0xff + IN_CLASSC_NET = 0xffffff00 + IN_CLASSC_NSHIFT = 0x8 + IN_CLASSD_HOST = 0xfffffff + IN_CLASSD_NET = 0xf0000000 + IN_CLASSD_NSHIFT = 0x1c + IN_LINKLOCALNETNUM = 0xa9fe0000 + IN_LOOPBACKNET = 0x7f + IPPROTO_3PC = 0x22 + IPPROTO_ADFS = 0x44 + IPPROTO_AH = 0x33 + IPPROTO_AHIP = 0x3d + IPPROTO_APES = 0x63 + IPPROTO_ARGUS = 0xd + IPPROTO_AX25 = 0x5d + IPPROTO_BHA = 0x31 + IPPROTO_BLT = 0x1e + IPPROTO_BRSATMON = 0x4c + IPPROTO_CFTP = 0x3e + IPPROTO_CHAOS = 0x10 + IPPROTO_CMTP = 0x26 + IPPROTO_CPHB = 0x49 + IPPROTO_CPNX = 0x48 + IPPROTO_DDP = 0x25 + IPPROTO_DGP = 0x56 + IPPROTO_DIVERT = 0xfe + IPPROTO_DONE = 0x101 + IPPROTO_DSTOPTS = 0x3c + IPPROTO_EGP = 0x8 + IPPROTO_EMCON = 0xe + IPPROTO_ENCAP = 0x62 + IPPROTO_EON = 0x50 + IPPROTO_ESP = 0x32 + IPPROTO_ETHERIP = 0x61 + IPPROTO_FRAGMENT = 0x2c + IPPROTO_GGP = 0x3 + IPPROTO_GMTP = 0x64 + IPPROTO_GRE = 0x2f + IPPROTO_HELLO = 0x3f + IPPROTO_HMP = 0x14 + IPPROTO_HOPOPTS = 0x0 + IPPROTO_ICMP = 0x1 + IPPROTO_ICMPV6 = 0x3a + IPPROTO_IDP = 0x16 + IPPROTO_IDPR = 0x23 + IPPROTO_IDRP = 0x2d + IPPROTO_IGMP = 0x2 + IPPROTO_IGP = 0x55 + IPPROTO_IGRP = 0x58 + IPPROTO_IL = 0x28 + IPPROTO_INLSP = 0x34 + IPPROTO_INP = 0x20 + IPPROTO_IP = 0x0 + IPPROTO_IPCOMP = 0x6c + IPPROTO_IPCV = 0x47 + IPPROTO_IPEIP = 0x5e + IPPROTO_IPIP = 0x4 + IPPROTO_IPPC = 0x43 + IPPROTO_IPV4 = 0x4 + IPPROTO_IPV6 = 0x29 + IPPROTO_IRTP = 0x1c + IPPROTO_KRYPTOLAN = 0x41 + IPPROTO_LARP = 0x5b + IPPROTO_LEAF1 = 0x19 + IPPROTO_LEAF2 = 0x1a + IPPROTO_MAX = 0x100 + IPPROTO_MAXID = 0x34 + IPPROTO_MEAS = 0x13 + IPPROTO_MHRP = 0x30 + IPPROTO_MICP = 0x5f + IPPROTO_MTP = 0x5c + IPPROTO_MUX = 0x12 + IPPROTO_ND = 0x4d + IPPROTO_NHRP = 0x36 + IPPROTO_NONE = 0x3b + IPPROTO_NSP = 0x1f + IPPROTO_NVPII = 0xb + IPPROTO_OSPFIGP = 0x59 + IPPROTO_PGM = 0x71 + IPPROTO_PIGP = 0x9 + IPPROTO_PIM = 0x67 + IPPROTO_PRM = 0x15 + IPPROTO_PUP = 0xc + IPPROTO_PVP = 0x4b + IPPROTO_RAW = 0xff + IPPROTO_RCCMON = 0xa + IPPROTO_RDP = 0x1b + IPPROTO_ROUTING = 0x2b + IPPROTO_RSVP = 0x2e + IPPROTO_RVD = 0x42 + IPPROTO_SATEXPAK = 0x40 + IPPROTO_SATMON = 0x45 + IPPROTO_SCCSP = 0x60 + IPPROTO_SDRP = 0x2a + IPPROTO_SEP = 0x21 + IPPROTO_SRPC = 0x5a + IPPROTO_ST = 0x7 + IPPROTO_SVMTP = 0x52 + IPPROTO_SWIPE = 0x35 + IPPROTO_TCF = 0x57 + IPPROTO_TCP = 0x6 + IPPROTO_TP = 0x1d + IPPROTO_TPXX = 0x27 + IPPROTO_TRUNK1 = 0x17 + IPPROTO_TRUNK2 = 0x18 + IPPROTO_TTP = 0x54 + IPPROTO_UDP = 0x11 + IPPROTO_VINES = 0x53 + IPPROTO_VISA = 0x46 + IPPROTO_VMTP = 0x51 + IPPROTO_WBEXPAK = 0x4f + IPPROTO_WBMON = 0x4e + IPPROTO_WSN = 0x4a + IPPROTO_XNET = 0xf + IPPROTO_XTP = 0x24 + IPV6_BINDV6ONLY = 0x1b + IPV6_CHECKSUM = 0x1a + IPV6_DEFAULT_MULTICAST_HOPS = 0x1 + IPV6_DEFAULT_MULTICAST_LOOP = 0x1 + IPV6_DEFHLIM = 0x40 + IPV6_DSTOPTS = 0x17 + IPV6_FAITH = 0x1d + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 + IPV6_FRAGTTL = 0x78 + IPV6_FW_ADD = 0x1e + IPV6_FW_DEL = 0x1f + IPV6_FW_FLUSH = 0x20 + IPV6_FW_GET = 0x22 + IPV6_FW_ZERO = 0x21 + IPV6_HLIMDEC = 0x1 + IPV6_HOPLIMIT = 0x14 + IPV6_HOPOPTS = 0x16 + IPV6_IPSEC_POLICY = 0x1c + IPV6_JOIN_GROUP = 0xc + IPV6_LEAVE_GROUP = 0xd + IPV6_MAXHLIM = 0xff + IPV6_MAXPACKET = 0xffff + IPV6_MMTU = 0x500 + IPV6_MULTICAST_HOPS = 0xa + IPV6_MULTICAST_IF = 0x9 + IPV6_MULTICAST_LOOP = 0xb + IPV6_NEXTHOP = 0x15 + IPV6_PKTINFO = 0x13 + IPV6_PKTOPTIONS = 0x19 + IPV6_PORTRANGE = 0xe + IPV6_PORTRANGE_DEFAULT = 0x0 + IPV6_PORTRANGE_HIGH = 0x1 + IPV6_PORTRANGE_LOW = 0x2 + IPV6_RTHDR = 0x18 + IPV6_RTHDR_LOOSE = 0x0 + IPV6_RTHDR_STRICT = 0x1 + IPV6_RTHDR_TYPE_0 = 0x0 + IPV6_SOCKOPT_RESERVED1 = 0x3 + IPV6_UNICAST_HOPS = 0x4 + IPV6_V6ONLY = 0x1b + IPV6_VERSION = 0x60 + IPV6_VERSION_MASK = 0xf0 + IP_ADD_MEMBERSHIP = 0xc + IP_DEFAULT_MULTICAST_LOOP = 0x1 + IP_DEFAULT_MULTICAST_TTL = 0x1 + IP_DF = 0x4000 + IP_DROP_MEMBERSHIP = 0xd + IP_DUMMYNET_CONFIGURE = 0x3c + IP_DUMMYNET_DEL = 0x3d + IP_DUMMYNET_FLUSH = 0x3e + IP_DUMMYNET_GET = 0x40 + IP_FAITH = 0x16 + IP_FW_ADD = 0x28 + IP_FW_DEL = 0x29 + IP_FW_FLUSH = 0x2a + IP_FW_GET = 0x2c + IP_FW_RESETLOG = 0x2d + IP_FW_ZERO = 0x2b + IP_HDRINCL = 0x2 + IP_IPSEC_POLICY = 0x15 + IP_MAXPACKET = 0xffff + IP_MAX_MEMBERSHIPS = 0x14 + IP_MF = 0x2000 + IP_MSS = 0x240 + IP_MULTICAST_IF = 0x9 + IP_MULTICAST_LOOP = 0xb + IP_MULTICAST_TTL = 0xa + IP_MULTICAST_VIF = 0xe + IP_NAT__XXX = 0x37 + IP_OFFMASK = 0x1fff + IP_OLD_FW_ADD = 0x32 + IP_OLD_FW_DEL = 0x33 + IP_OLD_FW_FLUSH = 0x34 + IP_OLD_FW_GET = 0x36 + IP_OLD_FW_RESETLOG = 0x38 + IP_OLD_FW_ZERO = 0x35 + IP_OPTIONS = 0x1 + IP_PKTINFO = 0x1a + IP_PORTRANGE = 0x13 + IP_PORTRANGE_DEFAULT = 0x0 + IP_PORTRANGE_HIGH = 0x1 + IP_PORTRANGE_LOW = 0x2 + IP_RECVDSTADDR = 0x7 + IP_RECVIF = 0x14 + IP_RECVOPTS = 0x5 + IP_RECVPKTINFO = 0x1a + IP_RECVRETOPTS = 0x6 + IP_RECVTTL = 0x18 + IP_RETOPTS = 0x8 + IP_RF = 0x8000 + IP_RSVP_OFF = 0x10 + IP_RSVP_ON = 0xf + IP_RSVP_VIF_OFF = 0x12 + IP_RSVP_VIF_ON = 0x11 + IP_STRIPHDR = 0x17 + IP_TOS = 0x3 + IP_TRAFFIC_MGT_BACKGROUND = 0x41 + IP_TTL = 0x4 + ISIG = 0x80 + ISTRIP = 0x20 + IUTF8 = 0x4000 + IXANY = 0x800 + IXOFF = 0x400 + IXON = 0x200 + LOCK_EX = 0x2 + LOCK_NB = 0x4 + LOCK_SH = 0x1 + LOCK_UN = 0x8 + MADV_DONTNEED = 0x4 + MADV_FREE = 0x5 + MADV_NORMAL = 0x0 + MADV_RANDOM = 0x1 + MADV_SEQUENTIAL = 0x2 + MADV_WILLNEED = 0x3 + MAP_ANON = 0x1000 + MAP_COPY = 0x2 + MAP_FILE = 0x0 + MAP_FIXED = 0x10 + MAP_HASSEMAPHORE = 0x200 + MAP_NOCACHE = 0x400 + MAP_NOEXTEND = 0x100 + MAP_NORESERVE = 0x40 + MAP_PRIVATE = 0x2 + MAP_RENAME = 0x20 + MAP_RESERVED0080 = 0x80 + MAP_SHARED = 0x1 + MCL_CURRENT = 0x1 + MCL_FUTURE = 0x2 + MSG_CTRUNC = 0x20 + MSG_DONTROUTE = 0x4 + MSG_DONTWAIT = 0x80 + MSG_EOF = 0x100 + MSG_EOR = 0x8 + MSG_FLUSH = 0x400 + MSG_HAVEMORE = 0x2000 + MSG_HOLD = 0x800 + MSG_NEEDSA = 0x10000 + MSG_OOB = 0x1 + MSG_PEEK = 0x2 + MSG_RCVMORE = 0x4000 + MSG_SEND = 0x1000 + MSG_TRUNC = 0x10 + MSG_WAITALL = 0x40 + MSG_WAITSTREAM = 0x200 + MS_ASYNC = 0x1 + MS_DEACTIVATE = 0x8 + MS_INVALIDATE = 0x2 + MS_KILLPAGES = 0x4 + MS_SYNC = 0x10 + NAME_MAX = 0xff + NET_RT_DUMP = 0x1 + NET_RT_DUMP2 = 0x7 + NET_RT_FLAGS = 0x2 + NET_RT_IFLIST = 0x3 + NET_RT_IFLIST2 = 0x6 + NET_RT_MAXID = 0x8 + NET_RT_STAT = 0x4 + NET_RT_TRASH = 0x5 + NOFLSH = 0x80000000 + NOTE_ABSOLUTE = 0x8 + NOTE_ATTRIB = 0x8 + NOTE_CHILD = 0x4 + NOTE_DELETE = 0x1 + NOTE_EXEC = 0x20000000 + NOTE_EXIT = 0x80000000 + NOTE_EXTEND = 0x4 + NOTE_FORK = 0x40000000 + NOTE_LINK = 0x10 + NOTE_LOWAT = 0x1 + NOTE_NSECONDS = 0x4 + NOTE_PCTRLMASK = -0x100000 + NOTE_PDATAMASK = 0xfffff + NOTE_REAP = 0x10000000 + NOTE_RENAME = 0x20 + NOTE_REVOKE = 0x40 + NOTE_SECONDS = 0x1 + NOTE_SIGNAL = 0x8000000 + NOTE_TRACK = 0x1 + NOTE_TRACKERR = 0x2 + NOTE_USECONDS = 0x2 + NOTE_WRITE = 0x2 + OCRNL = 0x10 + OFDEL = 0x20000 + OFILL = 0x80 + ONLCR = 0x2 + ONLRET = 0x40 + ONOCR = 0x20 + ONOEOT = 0x8 + OPOST = 0x1 + O_ACCMODE = 0x3 + O_ALERT = 0x20000000 + O_APPEND = 0x8 + O_ASYNC = 0x40 + O_CLOEXEC = 0x1000000 + O_CREAT = 0x200 + O_DIRECTORY = 0x100000 + O_EVTONLY = 0x8000 + O_EXCL = 0x800 + O_EXLOCK = 0x20 + O_FSYNC = 0x80 + O_NDELAY = 0x4 + O_NOCTTY = 0x20000 + O_NOFOLLOW = 0x100 + O_NONBLOCK = 0x4 + O_POPUP = 0x80000000 + O_RDONLY = 0x0 + O_RDWR = 0x2 + O_SHLOCK = 0x10 + O_SYMLINK = 0x200000 + O_SYNC = 0x80 + O_TRUNC = 0x400 + O_WRONLY = 0x1 + PARENB = 0x1000 + PARMRK = 0x8 + PARODD = 0x2000 + PENDIN = 0x20000000 + PRIO_PGRP = 0x1 + PRIO_PROCESS = 0x0 + PRIO_USER = 0x2 + PROT_EXEC = 0x4 + PROT_NONE = 0x0 + PROT_READ = 0x1 + PROT_WRITE = 0x2 + PT_ATTACH = 0xa + PT_ATTACHEXC = 0xe + PT_CONTINUE = 0x7 + PT_DENY_ATTACH = 0x1f + PT_DETACH = 0xb + PT_FIRSTMACH = 0x20 + PT_FORCEQUOTA = 0x1e + PT_KILL = 0x8 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_READ_U = 0x3 + PT_SIGEXC = 0xc + PT_STEP = 0x9 + PT_THUPDATE = 0xd + PT_TRACE_ME = 0x0 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + PT_WRITE_U = 0x6 + RLIMIT_AS = 0x5 + RLIMIT_CORE = 0x4 + RLIMIT_CPU = 0x0 + RLIMIT_DATA = 0x2 + RLIMIT_FSIZE = 0x1 + RLIMIT_NOFILE = 0x8 + RLIMIT_STACK = 0x3 + RLIM_INFINITY = 0x7fffffffffffffff + RTAX_AUTHOR = 0x6 + RTAX_BRD = 0x7 + RTAX_DST = 0x0 + RTAX_GATEWAY = 0x1 + RTAX_GENMASK = 0x3 + RTAX_IFA = 0x5 + RTAX_IFP = 0x4 + RTAX_MAX = 0x8 + RTAX_NETMASK = 0x2 + RTA_AUTHOR = 0x40 + RTA_BRD = 0x80 + RTA_DST = 0x1 + RTA_GATEWAY = 0x2 + RTA_GENMASK = 0x8 + RTA_IFA = 0x20 + RTA_IFP = 0x10 + RTA_NETMASK = 0x4 + RTF_BLACKHOLE = 0x1000 + RTF_BROADCAST = 0x400000 + RTF_CLONING = 0x100 + RTF_CONDEMNED = 0x2000000 + RTF_DELCLONE = 0x80 + RTF_DONE = 0x40 + RTF_DYNAMIC = 0x10 + RTF_GATEWAY = 0x2 + RTF_HOST = 0x4 + RTF_IFREF = 0x4000000 + RTF_IFSCOPE = 0x1000000 + RTF_LLINFO = 0x400 + RTF_LOCAL = 0x200000 + RTF_MODIFIED = 0x20 + RTF_MULTICAST = 0x800000 + RTF_PINNED = 0x100000 + RTF_PRCLONING = 0x10000 + RTF_PROTO1 = 0x8000 + RTF_PROTO2 = 0x4000 + RTF_PROTO3 = 0x40000 + RTF_REJECT = 0x8 + RTF_STATIC = 0x800 + RTF_UP = 0x1 + RTF_WASCLONED = 0x20000 + RTF_XRESOLVE = 0x200 + RTM_ADD = 0x1 + RTM_CHANGE = 0x3 + RTM_DELADDR = 0xd + RTM_DELETE = 0x2 + RTM_DELMADDR = 0x10 + RTM_GET = 0x4 + RTM_GET2 = 0x14 + RTM_IFINFO = 0xe + RTM_IFINFO2 = 0x12 + RTM_LOCK = 0x8 + RTM_LOSING = 0x5 + RTM_MISS = 0x7 + RTM_NEWADDR = 0xc + RTM_NEWMADDR = 0xf + RTM_NEWMADDR2 = 0x13 + RTM_OLDADD = 0x9 + RTM_OLDDEL = 0xa + RTM_REDIRECT = 0x6 + RTM_RESOLVE = 0xb + RTM_RTTUNIT = 0xf4240 + RTM_VERSION = 0x5 + RTV_EXPIRE = 0x4 + RTV_HOPCOUNT = 0x2 + RTV_MTU = 0x1 + RTV_RPIPE = 0x8 + RTV_RTT = 0x40 + RTV_RTTVAR = 0x80 + RTV_SPIPE = 0x10 + RTV_SSTHRESH = 0x20 + RUSAGE_CHILDREN = -0x1 + RUSAGE_SELF = 0x0 + SCM_CREDS = 0x3 + SCM_RIGHTS = 0x1 + SCM_TIMESTAMP = 0x2 + SHUT_RD = 0x0 + SHUT_RDWR = 0x2 + SHUT_WR = 0x1 + SIOCADDMULTI = 0x80206931 + SIOCAIFADDR = 0x8040691a + SIOCALIFADDR = 0x8118691d + SIOCARPIPLL = 0xc0206928 + SIOCATMARK = 0x40047307 + SIOCAUTOADDR = 0xc0206926 + SIOCAUTONETMASK = 0x80206927 + SIOCDELMULTI = 0x80206932 + SIOCDIFADDR = 0x80206919 + SIOCDIFPHYADDR = 0x80206941 + SIOCDLIFADDR = 0x8118691f + SIOCGETSGCNT = 0xc014721c + SIOCGETVIFCNT = 0xc014721b + SIOCGETVLAN = 0xc020697f + SIOCGHIWAT = 0x40047301 + SIOCGIFADDR = 0xc0206921 + SIOCGIFALTMTU = 0xc0206948 + SIOCGIFASYNCMAP = 0xc020697c + SIOCGIFBOND = 0xc0206947 + SIOCGIFBRDADDR = 0xc0206923 + SIOCGIFCONF = 0xc0086924 + SIOCGIFDEVMTU = 0xc0206944 + SIOCGIFDSTADDR = 0xc0206922 + SIOCGIFFLAGS = 0xc0206911 + SIOCGIFGENERIC = 0xc020693a + SIOCGIFKPI = 0xc0206987 + SIOCGIFMAC = 0xc0206982 + SIOCGIFMEDIA = 0xc0286938 + SIOCGIFMETRIC = 0xc0206917 + SIOCGIFMTU = 0xc0206933 + SIOCGIFNETMASK = 0xc0206925 + SIOCGIFPDSTADDR = 0xc0206940 + SIOCGIFPHYS = 0xc0206935 + SIOCGIFPSRCADDR = 0xc020693f + SIOCGIFSTATUS = 0xc331693d + SIOCGIFVLAN = 0xc020697f + SIOCGLIFADDR = 0xc118691e + SIOCGLIFPHYADDR = 0xc1186943 + SIOCGLOWAT = 0x40047303 + SIOCGPGRP = 0x40047309 + SIOCIFCREATE = 0xc0206978 + SIOCIFDESTROY = 0x80206979 + SIOCRSLVMULTI = 0xc008693b + SIOCSETVLAN = 0x8020697e + SIOCSHIWAT = 0x80047300 + SIOCSIFADDR = 0x8020690c + SIOCSIFALTMTU = 0x80206945 + SIOCSIFASYNCMAP = 0x8020697d + SIOCSIFBOND = 0x80206946 + SIOCSIFBRDADDR = 0x80206913 + SIOCSIFDSTADDR = 0x8020690e + SIOCSIFFLAGS = 0x80206910 + SIOCSIFGENERIC = 0x80206939 + SIOCSIFKPI = 0x80206986 + SIOCSIFLLADDR = 0x8020693c + SIOCSIFMAC = 0x80206983 + SIOCSIFMEDIA = 0xc0206937 + SIOCSIFMETRIC = 0x80206918 + SIOCSIFMTU = 0x80206934 + SIOCSIFNETMASK = 0x80206916 + SIOCSIFPHYADDR = 0x8040693e + SIOCSIFPHYS = 0x80206936 + SIOCSIFVLAN = 0x8020697e + SIOCSLIFPHYADDR = 0x81186942 + SIOCSLOWAT = 0x80047302 + SIOCSPGRP = 0x80047308 + SOCK_DGRAM = 0x2 + SOCK_MAXADDRLEN = 0xff + SOCK_RAW = 0x3 + SOCK_RDM = 0x4 + SOCK_SEQPACKET = 0x5 + SOCK_STREAM = 0x1 + SOL_SOCKET = 0xffff + SOMAXCONN = 0x80 + SO_ACCEPTCONN = 0x2 + SO_BROADCAST = 0x20 + SO_DEBUG = 0x1 + SO_DONTROUTE = 0x10 + SO_DONTTRUNC = 0x2000 + SO_ERROR = 0x1007 + SO_KEEPALIVE = 0x8 + SO_LABEL = 0x1010 + SO_LINGER = 0x80 + SO_LINGER_SEC = 0x1080 + SO_NKE = 0x1021 + SO_NOADDRERR = 0x1023 + SO_NOSIGPIPE = 0x1022 + SO_NOTIFYCONFLICT = 0x1026 + SO_NREAD = 0x1020 + SO_NWRITE = 0x1024 + SO_OOBINLINE = 0x100 + SO_PEERLABEL = 0x1011 + SO_RCVBUF = 0x1002 + SO_RCVLOWAT = 0x1004 + SO_RCVTIMEO = 0x1006 + SO_RESTRICTIONS = 0x1081 + SO_RESTRICT_DENYIN = 0x1 + SO_RESTRICT_DENYOUT = 0x2 + SO_RESTRICT_DENYSET = 0x80000000 + SO_REUSEADDR = 0x4 + SO_REUSEPORT = 0x200 + SO_REUSESHAREUID = 0x1025 + SO_SNDBUF = 0x1001 + SO_SNDLOWAT = 0x1003 + SO_SNDTIMEO = 0x1005 + SO_TIMESTAMP = 0x400 + SO_TYPE = 0x1008 + SO_UPCALLCLOSEWAIT = 0x1027 + SO_USELOOPBACK = 0x40 + SO_WANTMORE = 0x4000 + SO_WANTOOBFLAG = 0x8000 + S_IEXEC = 0x40 + S_IFBLK = 0x6000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFIFO = 0x1000 + S_IFLNK = 0xa000 + S_IFMT = 0xf000 + S_IFREG = 0x8000 + S_IFSOCK = 0xc000 + S_IFWHT = 0xe000 + S_IFXATTR = 0x10000 + S_IREAD = 0x100 + S_IRGRP = 0x20 + S_IROTH = 0x4 + S_IRUSR = 0x100 + S_IRWXG = 0x38 + S_IRWXO = 0x7 + S_IRWXU = 0x1c0 + S_ISGID = 0x400 + S_ISTXT = 0x200 + S_ISUID = 0x800 + S_ISVTX = 0x200 + S_IWGRP = 0x10 + S_IWOTH = 0x2 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXGRP = 0x8 + S_IXOTH = 0x1 + S_IXUSR = 0x40 + TCIFLUSH = 0x1 + TCIOFLUSH = 0x3 + TCOFLUSH = 0x2 + TCP_KEEPALIVE = 0x10 + TCP_MAXBURST = 0x4 + TCP_MAXHLEN = 0x3c + TCP_MAXOLEN = 0x28 + TCP_MAXSEG = 0x2 + TCP_MAXWIN = 0xffff + TCP_MAX_SACK = 0x3 + TCP_MAX_WINSHIFT = 0xe + TCP_MINMSS = 0xd8 + TCP_MINMSSOVERLOAD = 0x3e8 + TCP_MSS = 0x200 + TCP_NODELAY = 0x1 + TCP_NOOPT = 0x8 + TCP_NOPUSH = 0x4 + TCSAFLUSH = 0x2 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x40087458 + TIOCDRAIN = 0x2000745e + TIOCDSIMICROCODE = 0x20007455 + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGWINSZ = 0x40087468 + TIOCIXOFF = 0x20007480 + TIOCIXON = 0x20007481 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMODG = 0x40047403 + TIOCMODS = 0x80047404 + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0x0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTYGNAME = 0x40807453 + TIOCPTYGRANT = 0x20007454 + TIOCPTYUNLK = 0x20007452 + TIOCREMOTE = 0x80047469 + TIOCSBRK = 0x2000747b + TIOCSCONS = 0x20007463 + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2000745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40087459 + TIOCUCNTL = 0x80047466 + TOSTOP = 0x400000 + VDISCARD = 0xf + VDSUSP = 0xb + VEOF = 0x0 + VEOL = 0x1 + VEOL2 = 0x2 + VERASE = 0x3 + VINTR = 0x8 + VKILL = 0x5 + VLNEXT = 0xe + VMIN = 0x10 + VQUIT = 0x9 + VREPRINT = 0x6 + VSTART = 0xc + VSTATUS = 0x12 + VSTOP = 0xd + VSUSP = 0xa + VT0 = 0x0 + VT1 = 0x10000 + VTDLY = 0x10000 + VTIME = 0x11 + VWERASE = 0x4 + WCONTINUED = 0x10 + WCOREFLAG = 0x80 + WEXITED = 0x4 + WNOHANG = 0x1 + WNOWAIT = 0x20 + WORDSIZE = 0x20 + WSTOPPED = 0x8 + WUNTRACED = 0x2 +) + +// Errors +const ( + E2BIG = Errno(0x7) + EACCES = Errno(0xd) + EADDRINUSE = Errno(0x30) + EADDRNOTAVAIL = Errno(0x31) + EAFNOSUPPORT = Errno(0x2f) + EAGAIN = Errno(0x23) + EALREADY = Errno(0x25) + EAUTH = Errno(0x50) + EBADARCH = Errno(0x56) + EBADEXEC = Errno(0x55) + EBADF = Errno(0x9) + EBADMACHO = Errno(0x58) + EBADMSG = Errno(0x5e) + EBADRPC = Errno(0x48) + EBUSY = Errno(0x10) + ECANCELED = Errno(0x59) + ECHILD = Errno(0xa) + ECONNABORTED = Errno(0x35) + ECONNREFUSED = Errno(0x3d) + ECONNRESET = Errno(0x36) + EDEADLK = Errno(0xb) + EDESTADDRREQ = Errno(0x27) + EDEVERR = Errno(0x53) + EDOM = Errno(0x21) + EDQUOT = Errno(0x45) + EEXIST = Errno(0x11) + EFAULT = Errno(0xe) + EFBIG = Errno(0x1b) + EFTYPE = Errno(0x4f) + EHOSTDOWN = Errno(0x40) + EHOSTUNREACH = Errno(0x41) + EIDRM = Errno(0x5a) + EILSEQ = Errno(0x5c) + EINPROGRESS = Errno(0x24) + EINTR = Errno(0x4) + EINVAL = Errno(0x16) + EIO = Errno(0x5) + EISCONN = Errno(0x38) + EISDIR = Errno(0x15) + ELAST = Errno(0x67) + ELOOP = Errno(0x3e) + EMFILE = Errno(0x18) + EMLINK = Errno(0x1f) + EMSGSIZE = Errno(0x28) + EMULTIHOP = Errno(0x5f) + ENAMETOOLONG = Errno(0x3f) + ENEEDAUTH = Errno(0x51) + ENETDOWN = Errno(0x32) + ENETRESET = Errno(0x34) + ENETUNREACH = Errno(0x33) + ENFILE = Errno(0x17) + ENOATTR = Errno(0x5d) + ENOBUFS = Errno(0x37) + ENODATA = Errno(0x60) + ENODEV = Errno(0x13) + ENOENT = Errno(0x2) + ENOEXEC = Errno(0x8) + ENOLCK = Errno(0x4d) + ENOLINK = Errno(0x61) + ENOMEM = Errno(0xc) + ENOMSG = Errno(0x5b) + ENOPOLICY = Errno(0x67) + ENOPROTOOPT = Errno(0x2a) + ENOSPC = Errno(0x1c) + ENOSR = Errno(0x62) + ENOSTR = Errno(0x63) + ENOSYS = Errno(0x4e) + ENOTBLK = Errno(0xf) + ENOTCONN = Errno(0x39) + ENOTDIR = Errno(0x14) + ENOTEMPTY = Errno(0x42) + ENOTSOCK = Errno(0x26) + ENOTSUP = Errno(0x2d) + ENOTTY = Errno(0x19) + ENXIO = Errno(0x6) + EOPNOTSUPP = Errno(0x66) + EOVERFLOW = Errno(0x54) + EPERM = Errno(0x1) + EPFNOSUPPORT = Errno(0x2e) + EPIPE = Errno(0x20) + EPROCLIM = Errno(0x43) + EPROCUNAVAIL = Errno(0x4c) + EPROGMISMATCH = Errno(0x4b) + EPROGUNAVAIL = Errno(0x4a) + EPROTO = Errno(0x64) + EPROTONOSUPPORT = Errno(0x2b) + EPROTOTYPE = Errno(0x29) + EPWROFF = Errno(0x52) + ERANGE = Errno(0x22) + EREMOTE = Errno(0x47) + EROFS = Errno(0x1e) + ERPCMISMATCH = Errno(0x49) + ESHLIBVERS = Errno(0x57) + ESHUTDOWN = Errno(0x3a) + ESOCKTNOSUPPORT = Errno(0x2c) + ESPIPE = Errno(0x1d) + ESRCH = Errno(0x3) + ESTALE = Errno(0x46) + ETIME = Errno(0x65) + ETIMEDOUT = Errno(0x3c) + ETOOMANYREFS = Errno(0x3b) + ETXTBSY = Errno(0x1a) + EUSERS = Errno(0x44) + EWOULDBLOCK = Errno(0x23) + EXDEV = Errno(0x12) +) + +// Signals +const ( + SIGABRT = Signal(0x6) + SIGALRM = Signal(0xe) + SIGBUS = Signal(0xa) + SIGCHLD = Signal(0x14) + SIGCONT = Signal(0x13) + SIGEMT = Signal(0x7) + SIGFPE = Signal(0x8) + SIGHUP = Signal(0x1) + SIGILL = Signal(0x4) + SIGINFO = Signal(0x1d) + SIGINT = Signal(0x2) + SIGIO = Signal(0x17) + SIGIOT = Signal(0x6) + SIGKILL = Signal(0x9) + SIGPIPE = Signal(0xd) + SIGPROF = Signal(0x1b) + SIGQUIT = Signal(0x3) + SIGSEGV = Signal(0xb) + SIGSTOP = Signal(0x11) + SIGSYS = Signal(0xc) + SIGTERM = Signal(0xf) + SIGTRAP = Signal(0x5) + SIGTSTP = Signal(0x12) + SIGTTIN = Signal(0x15) + SIGTTOU = Signal(0x16) + SIGURG = Signal(0x10) + SIGUSR1 = Signal(0x1e) + SIGUSR2 = Signal(0x1f) + SIGVTALRM = Signal(0x1a) + SIGWINCH = Signal(0x1c) + SIGXCPU = Signal(0x18) + SIGXFSZ = Signal(0x19) +) + +// Error table +var errors = [...]string{ + 1: "operation not permitted", + 2: "no such file or directory", + 3: "no such process", + 4: "interrupted system call", + 5: "input/output error", + 6: "device not configured", + 7: "argument list too long", + 8: "exec format error", + 9: "bad file descriptor", + 10: "no child processes", + 11: "resource deadlock avoided", + 12: "cannot allocate memory", + 13: "permission denied", + 14: "bad address", + 15: "block device required", + 16: "resource busy", + 17: "file exists", + 18: "cross-device link", + 19: "operation not supported by device", + 20: "not a directory", + 21: "is a directory", + 22: "invalid argument", + 23: "too many open files in system", + 24: "too many open files", + 25: "inappropriate ioctl for device", + 26: "text file busy", + 27: "file too large", + 28: "no space left on device", + 29: "illegal seek", + 30: "read-only file system", + 31: "too many links", + 32: "broken pipe", + 33: "numerical argument out of domain", + 34: "result too large", + 35: "resource temporarily unavailable", + 36: "operation now in progress", + 37: "operation already in progress", + 38: "socket operation on non-socket", + 39: "destination address required", + 40: "message too long", + 41: "protocol wrong type for socket", + 42: "protocol not available", + 43: "protocol not supported", + 44: "socket type not supported", + 45: "operation not supported", + 46: "protocol family not supported", + 47: "address family not supported by protocol family", + 48: "address already in use", + 49: "can't assign requested address", + 50: "network is down", + 51: "network is unreachable", + 52: "network dropped connection on reset", + 53: "software caused connection abort", + 54: "connection reset by peer", + 55: "no buffer space available", + 56: "socket is already connected", + 57: "socket is not connected", + 58: "can't send after socket shutdown", + 59: "too many references: can't splice", + 60: "operation timed out", + 61: "connection refused", + 62: "too many levels of symbolic links", + 63: "file name too long", + 64: "host is down", + 65: "no route to host", + 66: "directory not empty", + 67: "too many processes", + 68: "too many users", + 69: "disc quota exceeded", + 70: "stale NFS file handle", + 71: "too many levels of remote in path", + 72: "RPC struct is bad", + 73: "RPC version wrong", + 74: "RPC prog. not avail", + 75: "program version wrong", + 76: "bad procedure for program", + 77: "no locks available", + 78: "function not implemented", + 79: "inappropriate file type or format", + 80: "authentication error", + 81: "need authenticator", + 82: "device power is off", + 83: "device error", + 84: "value too large to be stored in data type", + 85: "bad executable (or shared library)", + 86: "bad CPU type in executable", + 87: "shared library version mismatch", + 88: "malformed Mach-o file", + 89: "operation canceled", + 90: "identifier removed", + 91: "no message of desired type", + 92: "illegal byte sequence", + 93: "attribute not found", + 94: "bad message", + 95: "EMULTIHOP (Reserved)", + 96: "no message available on STREAM", + 97: "ENOLINK (Reserved)", + 98: "no STREAM resources", + 99: "not a STREAM", + 100: "protocol error", + 101: "STREAM ioctl timeout", + 102: "operation not supported on socket", + 103: "policy not found", +} + +// Signal table +var signals = [...]string{ + 1: "hangup", + 2: "interrupt", + 3: "quit", + 4: "illegal instruction", + 5: "trace/BPT trap", + 6: "abort trap", + 7: "EMT trap", + 8: "floating point exception", + 9: "killed", + 10: "bus error", + 11: "segmentation fault", + 12: "bad system call", + 13: "broken pipe", + 14: "alarm clock", + 15: "terminated", + 16: "urgent I/O condition", + 17: "suspended (signal)", + 18: "suspended", + 19: "continued", + 20: "child exited", + 21: "stopped (tty input)", + 22: "stopped (tty output)", + 23: "I/O possible", + 24: "cputime limit exceeded", + 25: "filesize limit exceeded", + 26: "virtual timer expired", + 27: "profiling timer expired", + 28: "window size changes", + 29: "information request", + 30: "user defined signal 1", + 31: "user defined signal 2", +} diff --git a/src/syscall/zsyscall_darwin_arm.go b/src/syscall/zsyscall_darwin_arm.go new file mode 100644 index 0000000000..d851d69b43 --- /dev/null +++ b/src/syscall/zsyscall_darwin_arm.go @@ -0,0 +1,1324 @@ +// mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_arm.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import "unsafe" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getgroups(ngid int, gid *_Gid_t) (n int, err error) { + r0, _, e1 := RawSyscall(SYS_GETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setgroups(ngid int, gid *_Gid_t) (err error) { + _, _, e1 := RawSyscall(SYS_SETGROUPS, uintptr(ngid), uintptr(unsafe.Pointer(gid)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func wait4(pid int, wstatus *_C_int, options int, rusage *Rusage) (wpid int, err error) { + r0, _, e1 := Syscall6(SYS_WAIT4, uintptr(pid), uintptr(unsafe.Pointer(wstatus)), uintptr(options), uintptr(unsafe.Pointer(rusage)), 0, 0) + wpid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (fd int, err error) { + r0, _, e1 := Syscall(SYS_ACCEPT, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func bind(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_BIND, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func connect(s int, addr unsafe.Pointer, addrlen _Socklen) (err error) { + _, _, e1 := Syscall(SYS_CONNECT, uintptr(s), uintptr(addr), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socket(domain int, typ int, proto int) (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_SOCKET, uintptr(domain), uintptr(typ), uintptr(proto)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) (err error) { + _, _, e1 := Syscall6(SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) (err error) { + _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETPEERNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (err error) { + _, _, e1 := RawSyscall(SYS_GETSOCKNAME, uintptr(fd), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Shutdown(s int, how int) (err error) { + _, _, e1 := Syscall(SYS_SHUTDOWN, uintptr(s), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func socketpair(domain int, typ int, proto int, fd *[2]int32) (err error) { + _, _, e1 := RawSyscall6(SYS_SOCKETPAIR, uintptr(domain), uintptr(typ), uintptr(proto), uintptr(unsafe.Pointer(fd)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_RECVFROM, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS_SENDTO, uintptr(s), uintptr(_p0), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(addrlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func recvmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (n int, err error) { + r0, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, nevent int, timeout *Timespec) (n int, err error) { + r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) { + var _p0 unsafe.Pointer + if len(mib) > 0 { + _p0 = unsafe.Pointer(&mib[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := Syscall6(SYS___SYSCTL, uintptr(_p0), uintptr(len(mib)), uintptr(unsafe.Pointer(old)), uintptr(unsafe.Pointer(oldlen)), uintptr(unsafe.Pointer(new)), uintptr(newlen)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func utimes(path string, timeval *[2]Timeval) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UTIMES, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func futimes(fd int, timeval *[2]Timeval) (err error) { + _, _, e1 := Syscall(SYS_FUTIMES, uintptr(fd), uintptr(unsafe.Pointer(timeval)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntl(fd int, cmd int, arg int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FCNTL, uintptr(fd), uintptr(cmd), uintptr(arg)) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ptrace(request int, pid int, addr uintptr, data uintptr) (err error) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pipe() (r int, w int, err error) { + r0, r1, e1 := RawSyscall(SYS_PIPE, 0, 0, 0) + r = int(r0) + w = int(r1) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func kill(pid int, signum int, posix int) (err error) { + _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Access(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Adjtime(delta *Timeval, olddelta *Timeval) (err error) { + _, _, e1 := Syscall(SYS_ADJTIME, uintptr(unsafe.Pointer(delta)), uintptr(unsafe.Pointer(olddelta)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chmod(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHMOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Chroot(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_CHROOT, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Close(fd int) (err error) { + _, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup(fd int) (nfd int, err error) { + r0, _, e1 := RawSyscall(SYS_DUP, uintptr(fd), 0, 0) + nfd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Dup2(from int, to int) (err error) { + _, _, e1 := RawSyscall(SYS_DUP2, uintptr(from), uintptr(to), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Exchangedata(path1 string, path2 string, options int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path1) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(path2) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_EXCHANGEDATA, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Exit(code int) { + Syscall(SYS_EXIT, uintptr(code), 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchdir(fd int) (err error) { + _, _, e1 := Syscall(SYS_FCHDIR, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchflags(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchmod(fd int, mode uint32) (err error) { + _, _, e1 := Syscall(SYS_FCHMOD, uintptr(fd), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fchown(fd int, uid int, gid int) (err error) { + _, _, e1 := Syscall(SYS_FCHOWN, uintptr(fd), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Flock(fd int, how int) (err error) { + _, _, e1 := Syscall(SYS_FLOCK, uintptr(fd), uintptr(how), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fpathconf(fd int, name int) (val int, err error) { + r0, _, e1 := Syscall(SYS_FPATHCONF, uintptr(fd), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstat(fd int, stat *Stat_t) (err error) { + _, _, e1 := Syscall(SYS_FSTAT64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fstatfs(fd int, stat *Statfs_t) (err error) { + _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Fsync(fd int) (err error) { + _, _, e1 := Syscall(SYS_FSYNC, uintptr(fd), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Ftruncate(fd int, length int64) (err error) { + _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), uintptr(length>>32)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) { + var _p0 unsafe.Pointer + if len(buf) > 0 { + _p0 = unsafe.Pointer(&buf[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_GETDIRENTRIES64, uintptr(fd), uintptr(_p0), uintptr(len(buf)), uintptr(unsafe.Pointer(basep)), 0, 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getdtablesize() (size int) { + r0, _, _ := Syscall(SYS_GETDTABLESIZE, 0, 0, 0) + size = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getegid() (egid int) { + r0, _, _ := RawSyscall(SYS_GETEGID, 0, 0, 0) + egid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Geteuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETEUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getgid() (gid int) { + r0, _, _ := RawSyscall(SYS_GETGID, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgid(pid int) (pgid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETPGID, uintptr(pid), 0, 0) + pgid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpgrp() (pgrp int) { + r0, _, _ := RawSyscall(SYS_GETPGRP, 0, 0, 0) + pgrp = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpid() (pid int) { + r0, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getppid() (ppid int) { + r0, _, _ := RawSyscall(SYS_GETPPID, 0, 0, 0) + ppid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getpriority(which int, who int) (prio int, err error) { + r0, _, e1 := Syscall(SYS_GETPRIORITY, uintptr(which), uintptr(who), 0) + prio = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_GETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getrusage(who int, rusage *Rusage) (err error) { + _, _, e1 := RawSyscall(SYS_GETRUSAGE, uintptr(who), uintptr(unsafe.Pointer(rusage)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getsid(pid int) (sid int, err error) { + r0, _, e1 := RawSyscall(SYS_GETSID, uintptr(pid), 0, 0) + sid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Getuid() (uid int) { + r0, _, _ := RawSyscall(SYS_GETUID, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Issetugid() (tainted bool) { + r0, _, _ := RawSyscall(SYS_ISSETUGID, 0, 0, 0) + tainted = bool(r0 != 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Kqueue() (fd int, err error) { + r0, _, e1 := Syscall(SYS_KQUEUE, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lchown(path string, uid int, gid int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LCHOWN, uintptr(unsafe.Pointer(_p0)), uintptr(uid), uintptr(gid)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Link(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Listen(s int, backlog int) (err error) { + _, _, e1 := Syscall(SYS_LISTEN, uintptr(s), uintptr(backlog), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Lstat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_LSTAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkdir(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKDIR, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mkfifo(path string, mode uint32) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKFIFO, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Mknod(path string, mode uint32, dev int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_MKNOD, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(dev)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Open(path string, mode int, perm uint32) (fd int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm)) + fd = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pathconf(path string, name int) (val int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + r0, _, e1 := Syscall(SYS_PATHCONF, uintptr(unsafe.Pointer(_p0)), uintptr(name), 0) + val = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pread(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Pwrite(fd int, p []byte, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func read(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Readlink(path string, buf []byte) (n int, err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 unsafe.Pointer + if len(buf) > 0 { + _p1 = unsafe.Pointer(&buf[0]) + } else { + _p1 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_READLINK, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(buf))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rename(from string, to string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(from) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(to) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RENAME, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Revoke(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_REVOKE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Rmdir(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_RMDIR, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { + r0, r1, e1 := Syscall6(SYS_LSEEK, uintptr(fd), uintptr(offset), uintptr(offset>>32), uintptr(whence), 0, 0) + newoffset = int64(int64(r1)<<32 | int64(r0)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Select(n int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (err error) { + _, _, e1 := Syscall6(SYS_SELECT, uintptr(n), uintptr(unsafe.Pointer(r)), uintptr(unsafe.Pointer(w)), uintptr(unsafe.Pointer(e)), uintptr(unsafe.Pointer(timeout)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setegid(egid int) (err error) { + _, _, e1 := Syscall(SYS_SETEGID, uintptr(egid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Seteuid(euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETEUID, uintptr(euid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setgid(gid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETGID, uintptr(gid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setlogin(name string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(name) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SETLOGIN, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpgid(pid int, pgid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETPGID, uintptr(pid), uintptr(pgid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setpriority(which int, who int, prio int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIORITY, uintptr(which), uintptr(who), uintptr(prio)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setprivexec(flag int) (err error) { + _, _, e1 := Syscall(SYS_SETPRIVEXEC, uintptr(flag), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setregid(rgid int, egid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREGID, uintptr(rgid), uintptr(egid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setreuid(ruid int, euid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETREUID, uintptr(ruid), uintptr(euid), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setrlimit(which int, lim *Rlimit) (err error) { + _, _, e1 := RawSyscall(SYS_SETRLIMIT, uintptr(which), uintptr(unsafe.Pointer(lim)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setsid() (pid int, err error) { + r0, _, e1 := RawSyscall(SYS_SETSID, 0, 0, 0) + pid = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Settimeofday(tp *Timeval) (err error) { + _, _, e1 := RawSyscall(SYS_SETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Setuid(uid int) (err error) { + _, _, e1 := RawSyscall(SYS_SETUID, uintptr(uid), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Stat(path string, stat *Stat_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STAT64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Statfs(path string, stat *Statfs_t) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Symlink(path string, link string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + var _p1 *byte + _p1, err = BytePtrFromString(link) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_SYMLINK, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Sync() (err error) { + _, _, e1 := Syscall(SYS_SYNC, 0, 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Truncate(path string, length int64) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_TRUNCATE, uintptr(unsafe.Pointer(_p0)), uintptr(length), uintptr(length>>32)) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Umask(newmask int) (oldmask int) { + r0, _, _ := Syscall(SYS_UMASK, uintptr(newmask), 0, 0) + oldmask = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Undelete(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNDELETE, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unlink(path string) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNLINK, uintptr(unsafe.Pointer(_p0)), 0, 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func Unmount(path string, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func write(fd int, p []byte) (n int, err error) { + var _p0 unsafe.Pointer + if len(p) > 0 { + _p0 = unsafe.Pointer(&p[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(_p0), uintptr(len(p))) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) { + r0, _, e1 := Syscall9(SYS_MMAP, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flag), uintptr(fd), uintptr(pos), uintptr(pos>>32), 0, 0) + ret = uintptr(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func munmap(addr uintptr, length uintptr) (err error) { + _, _, e1 := Syscall(SYS_MUNMAP, uintptr(addr), uintptr(length), 0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func readlen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writelen(fd int, buf *byte, nbuf int) (n int, err error) { + r0, _, e1 := Syscall(SYS_WRITE, uintptr(fd), uintptr(unsafe.Pointer(buf)), uintptr(nbuf)) + n = int(r0) + if e1 != 0 { + err = e1 + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func gettimeofday(tp *Timeval) (sec int32, usec int32, err error) { + r0, r1, e1 := RawSyscall(SYS_GETTIMEOFDAY, uintptr(unsafe.Pointer(tp)), 0, 0) + sec = int32(r0) + usec = int32(r1) + if e1 != 0 { + err = e1 + } + return +} diff --git a/src/syscall/zsysnum_darwin_arm.go b/src/syscall/zsysnum_darwin_arm.go new file mode 100644 index 0000000000..1a53f13eff --- /dev/null +++ b/src/syscall/zsysnum_darwin_arm.go @@ -0,0 +1,345 @@ +// mksysnum_darwin.pl /usr/include/sys/syscall.h +// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT + +package syscall + +const ( + SYS_SYSCALL = 0 + SYS_EXIT = 1 + SYS_FORK = 2 + SYS_READ = 3 + SYS_WRITE = 4 + SYS_OPEN = 5 + SYS_CLOSE = 6 + SYS_WAIT4 = 7 + SYS_LINK = 9 + SYS_UNLINK = 10 + SYS_CHDIR = 12 + SYS_FCHDIR = 13 + SYS_MKNOD = 14 + SYS_CHMOD = 15 + SYS_CHOWN = 16 + SYS_OBREAK = 17 + SYS_OGETFSSTAT = 18 + SYS_GETFSSTAT = 18 + SYS_GETPID = 20 + SYS_SETUID = 23 + SYS_GETUID = 24 + SYS_GETEUID = 25 + SYS_PTRACE = 26 + SYS_RECVMSG = 27 + SYS_SENDMSG = 28 + SYS_RECVFROM = 29 + SYS_ACCEPT = 30 + SYS_GETPEERNAME = 31 + SYS_GETSOCKNAME = 32 + SYS_ACCESS = 33 + SYS_CHFLAGS = 34 + SYS_FCHFLAGS = 35 + SYS_SYNC = 36 + SYS_KILL = 37 + SYS_GETPPID = 39 + SYS_DUP = 41 + SYS_PIPE = 42 + SYS_GETEGID = 43 + SYS_PROFIL = 44 + SYS_SIGACTION = 46 + SYS_GETGID = 47 + SYS_SIGPROCMASK = 48 + SYS_GETLOGIN = 49 + SYS_SETLOGIN = 50 + SYS_ACCT = 51 + SYS_SIGPENDING = 52 + SYS_SIGALTSTACK = 53 + SYS_IOCTL = 54 + SYS_REBOOT = 55 + SYS_REVOKE = 56 + SYS_SYMLINK = 57 + SYS_READLINK = 58 + SYS_EXECVE = 59 + SYS_UMASK = 60 + SYS_CHROOT = 61 + SYS_MSYNC = 65 + SYS_VFORK = 66 + SYS_SBRK = 69 + SYS_SSTK = 70 + SYS_OVADVISE = 72 + SYS_MUNMAP = 73 + SYS_MPROTECT = 74 + SYS_MADVISE = 75 + SYS_MINCORE = 78 + SYS_GETGROUPS = 79 + SYS_SETGROUPS = 80 + SYS_GETPGRP = 81 + SYS_SETPGID = 82 + SYS_SETITIMER = 83 + SYS_SWAPON = 85 + SYS_GETITIMER = 86 + SYS_GETDTABLESIZE = 89 + SYS_DUP2 = 90 + SYS_FCNTL = 92 + SYS_SELECT = 93 + SYS_FSYNC = 95 + SYS_SETPRIORITY = 96 + SYS_SOCKET = 97 + SYS_CONNECT = 98 + SYS_GETPRIORITY = 100 + SYS_BIND = 104 + SYS_SETSOCKOPT = 105 + SYS_LISTEN = 106 + SYS_SIGSUSPEND = 111 + SYS_GETTIMEOFDAY = 116 + SYS_GETRUSAGE = 117 + SYS_GETSOCKOPT = 118 + SYS_READV = 120 + SYS_WRITEV = 121 + SYS_SETTIMEOFDAY = 122 + SYS_FCHOWN = 123 + SYS_FCHMOD = 124 + SYS_SETREUID = 126 + SYS_SETREGID = 127 + SYS_RENAME = 128 + SYS_FLOCK = 131 + SYS_MKFIFO = 132 + SYS_SENDTO = 133 + SYS_SHUTDOWN = 134 + SYS_SOCKETPAIR = 135 + SYS_MKDIR = 136 + SYS_RMDIR = 137 + SYS_UTIMES = 138 + SYS_FUTIMES = 139 + SYS_ADJTIME = 140 + SYS_GETHOSTUUID = 142 + SYS_SETSID = 147 + SYS_GETPGID = 151 + SYS_SETPRIVEXEC = 152 + SYS_PREAD = 153 + SYS_PWRITE = 154 + SYS_NFSSVC = 155 + SYS_STATFS = 157 + SYS_FSTATFS = 158 + SYS_UNMOUNT = 159 + SYS_GETFH = 161 + SYS_QUOTACTL = 165 + SYS_MOUNT = 167 + SYS_CSOPS = 169 + SYS_TABLE = 170 + SYS_WAITID = 173 + SYS_ADD_PROFIL = 176 + SYS_KDEBUG_TRACE = 180 + SYS_SETGID = 181 + SYS_SETEGID = 182 + SYS_SETEUID = 183 + SYS_SIGRETURN = 184 + SYS_CHUD = 185 + SYS_STAT = 188 + SYS_FSTAT = 189 + SYS_LSTAT = 190 + SYS_PATHCONF = 191 + SYS_FPATHCONF = 192 + SYS_GETRLIMIT = 194 + SYS_SETRLIMIT = 195 + SYS_GETDIRENTRIES = 196 + SYS_MMAP = 197 + SYS_LSEEK = 199 + SYS_TRUNCATE = 200 + SYS_FTRUNCATE = 201 + SYS___SYSCTL = 202 + SYS_MLOCK = 203 + SYS_MUNLOCK = 204 + SYS_UNDELETE = 205 + SYS_ATSOCKET = 206 + SYS_ATGETMSG = 207 + SYS_ATPUTMSG = 208 + SYS_ATPSNDREQ = 209 + SYS_ATPSNDRSP = 210 + SYS_ATPGETREQ = 211 + SYS_ATPGETRSP = 212 + SYS_KQUEUE_FROM_PORTSET_NP = 214 + SYS_KQUEUE_PORTSET_NP = 215 + SYS_MKCOMPLEX = 216 + SYS_STATV = 217 + SYS_LSTATV = 218 + SYS_FSTATV = 219 + SYS_GETATTRLIST = 220 + SYS_SETATTRLIST = 221 + SYS_GETDIRENTRIESATTR = 222 + SYS_EXCHANGEDATA = 223 + SYS_SEARCHFS = 225 + SYS_DELETE = 226 + SYS_COPYFILE = 227 + SYS_POLL = 230 + SYS_WATCHEVENT = 231 + SYS_WAITEVENT = 232 + SYS_MODWATCH = 233 + SYS_GETXATTR = 234 + SYS_FGETXATTR = 235 + SYS_SETXATTR = 236 + SYS_FSETXATTR = 237 + SYS_REMOVEXATTR = 238 + SYS_FREMOVEXATTR = 239 + SYS_LISTXATTR = 240 + SYS_FLISTXATTR = 241 + SYS_FSCTL = 242 + SYS_INITGROUPS = 243 + SYS_POSIX_SPAWN = 244 + SYS_NFSCLNT = 247 + SYS_FHOPEN = 248 + SYS_MINHERIT = 250 + SYS_SEMSYS = 251 + SYS_MSGSYS = 252 + SYS_SHMSYS = 253 + SYS_SEMCTL = 254 + SYS_SEMGET = 255 + SYS_SEMOP = 256 + SYS_MSGCTL = 258 + SYS_MSGGET = 259 + SYS_MSGSND = 260 + SYS_MSGRCV = 261 + SYS_SHMAT = 262 + SYS_SHMCTL = 263 + SYS_SHMDT = 264 + SYS_SHMGET = 265 + SYS_SHM_OPEN = 266 + SYS_SHM_UNLINK = 267 + SYS_SEM_OPEN = 268 + SYS_SEM_CLOSE = 269 + SYS_SEM_UNLINK = 270 + SYS_SEM_WAIT = 271 + SYS_SEM_TRYWAIT = 272 + SYS_SEM_POST = 273 + SYS_SEM_GETVALUE = 274 + SYS_SEM_INIT = 275 + SYS_SEM_DESTROY = 276 + SYS_OPEN_EXTENDED = 277 + SYS_UMASK_EXTENDED = 278 + SYS_STAT_EXTENDED = 279 + SYS_LSTAT_EXTENDED = 280 + SYS_FSTAT_EXTENDED = 281 + SYS_CHMOD_EXTENDED = 282 + SYS_FCHMOD_EXTENDED = 283 + SYS_ACCESS_EXTENDED = 284 + SYS_SETTID = 285 + SYS_GETTID = 286 + SYS_SETSGROUPS = 287 + SYS_GETSGROUPS = 288 + SYS_SETWGROUPS = 289 + SYS_GETWGROUPS = 290 + SYS_MKFIFO_EXTENDED = 291 + SYS_MKDIR_EXTENDED = 292 + SYS_IDENTITYSVC = 293 + SYS_SHARED_REGION_CHECK_NP = 294 + SYS_SHARED_REGION_MAP_NP = 295 + SYS___PTHREAD_MUTEX_DESTROY = 301 + SYS___PTHREAD_MUTEX_INIT = 302 + SYS___PTHREAD_MUTEX_LOCK = 303 + SYS___PTHREAD_MUTEX_TRYLOCK = 304 + SYS___PTHREAD_MUTEX_UNLOCK = 305 + SYS___PTHREAD_COND_INIT = 306 + SYS___PTHREAD_COND_DESTROY = 307 + SYS___PTHREAD_COND_BROADCAST = 308 + SYS___PTHREAD_COND_SIGNAL = 309 + SYS_GETSID = 310 + SYS_SETTID_WITH_PID = 311 + SYS___PTHREAD_COND_TIMEDWAIT = 312 + SYS_AIO_FSYNC = 313 + SYS_AIO_RETURN = 314 + SYS_AIO_SUSPEND = 315 + SYS_AIO_CANCEL = 316 + SYS_AIO_ERROR = 317 + SYS_AIO_READ = 318 + SYS_AIO_WRITE = 319 + SYS_LIO_LISTIO = 320 + SYS___PTHREAD_COND_WAIT = 321 + SYS_IOPOLICYSYS = 322 + SYS_MLOCKALL = 324 + SYS_MUNLOCKALL = 325 + SYS_ISSETUGID = 327 + SYS___PTHREAD_KILL = 328 + SYS___PTHREAD_SIGMASK = 329 + SYS___SIGWAIT = 330 + SYS___DISABLE_THREADSIGNAL = 331 + SYS___PTHREAD_MARKCANCEL = 332 + SYS___PTHREAD_CANCELED = 333 + SYS___SEMWAIT_SIGNAL = 334 + SYS_PROC_INFO = 336 + SYS_SENDFILE = 337 + SYS_STAT64 = 338 + SYS_FSTAT64 = 339 + SYS_LSTAT64 = 340 + SYS_STAT64_EXTENDED = 341 + SYS_LSTAT64_EXTENDED = 342 + SYS_FSTAT64_EXTENDED = 343 + SYS_GETDIRENTRIES64 = 344 + SYS_STATFS64 = 345 + SYS_FSTATFS64 = 346 + SYS_GETFSSTAT64 = 347 + SYS___PTHREAD_CHDIR = 348 + SYS___PTHREAD_FCHDIR = 349 + SYS_AUDIT = 350 + SYS_AUDITON = 351 + SYS_GETAUID = 353 + SYS_SETAUID = 354 + SYS_GETAUDIT = 355 + SYS_SETAUDIT = 356 + SYS_GETAUDIT_ADDR = 357 + SYS_SETAUDIT_ADDR = 358 + SYS_AUDITCTL = 359 + SYS_BSDTHREAD_CREATE = 360 + SYS_BSDTHREAD_TERMINATE = 361 + SYS_KQUEUE = 362 + SYS_KEVENT = 363 + SYS_LCHOWN = 364 + SYS_STACK_SNAPSHOT = 365 + SYS_BSDTHREAD_REGISTER = 366 + SYS_WORKQ_OPEN = 367 + SYS_WORKQ_OPS = 368 + SYS___MAC_EXECVE = 380 + SYS___MAC_SYSCALL = 381 + SYS___MAC_GET_FILE = 382 + SYS___MAC_SET_FILE = 383 + SYS___MAC_GET_LINK = 384 + SYS___MAC_SET_LINK = 385 + SYS___MAC_GET_PROC = 386 + SYS___MAC_SET_PROC = 387 + SYS___MAC_GET_FD = 388 + SYS___MAC_SET_FD = 389 + SYS___MAC_GET_PID = 390 + SYS___MAC_GET_LCID = 391 + SYS___MAC_GET_LCTX = 392 + SYS___MAC_SET_LCTX = 393 + SYS_SETLCID = 394 + SYS_GETLCID = 395 + SYS_READ_NOCANCEL = 396 + SYS_WRITE_NOCANCEL = 397 + SYS_OPEN_NOCANCEL = 398 + SYS_CLOSE_NOCANCEL = 399 + SYS_WAIT4_NOCANCEL = 400 + SYS_RECVMSG_NOCANCEL = 401 + SYS_SENDMSG_NOCANCEL = 402 + SYS_RECVFROM_NOCANCEL = 403 + SYS_ACCEPT_NOCANCEL = 404 + SYS_MSYNC_NOCANCEL = 405 + SYS_FCNTL_NOCANCEL = 406 + SYS_SELECT_NOCANCEL = 407 + SYS_FSYNC_NOCANCEL = 408 + SYS_CONNECT_NOCANCEL = 409 + SYS_SIGSUSPEND_NOCANCEL = 410 + SYS_READV_NOCANCEL = 411 + SYS_WRITEV_NOCANCEL = 412 + SYS_SENDTO_NOCANCEL = 413 + SYS_PREAD_NOCANCEL = 414 + SYS_PWRITE_NOCANCEL = 415 + SYS_WAITID_NOCANCEL = 416 + SYS_POLL_NOCANCEL = 417 + SYS_MSGSND_NOCANCEL = 418 + SYS_MSGRCV_NOCANCEL = 419 + SYS_SEM_WAIT_NOCANCEL = 420 + SYS_AIO_SUSPEND_NOCANCEL = 421 + SYS___SIGWAIT_NOCANCEL = 422 + SYS___SEMWAIT_SIGNAL_NOCANCEL = 423 + SYS___MAC_MOUNT = 424 + SYS___MAC_GET_MOUNT = 425 + SYS___MAC_GETFSSTAT = 426 + SYS_MAXSYSCALL = 427 +) diff --git a/src/syscall/ztypes_darwin_arm.go b/src/syscall/ztypes_darwin_arm.go new file mode 100644 index 0000000000..ec87f54fb2 --- /dev/null +++ b/src/syscall/ztypes_darwin_arm.go @@ -0,0 +1,447 @@ +// NOTE: cgo can't generate struct Stat_t and struct Statfs_t yet +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_darwin.go + +package syscall + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type Timespec struct { + Sec int32 + Nsec int32 +} + +type Timeval struct { + Sec int32 + Usec int32 +} + +type Timeval32 [0]byte + +type Rusage struct { + Utime Timeval + Stime Timeval + Maxrss int32 + Ixrss int32 + Idrss int32 + Isrss int32 + Minflt int32 + Majflt int32 + Nswap int32 + Inblock int32 + Oublock int32 + Msgsnd int32 + Msgrcv int32 + Nsignals int32 + Nvcsw int32 + Nivcsw int32 +} + +type Rlimit struct { + Cur uint64 + Max uint64 +} + +type _Gid_t uint32 + +type Stat_t struct { + Dev int32 + Mode uint16 + Nlink uint16 + Ino uint64 + Uid uint32 + Gid uint32 + Rdev int32 + Atimespec Timespec + Mtimespec Timespec + Ctimespec Timespec + Birthtimespec Timespec + Size int64 + Blocks int64 + Blksize int32 + Flags uint32 + Gen uint32 + Lspare int32 + Qspare [2]int64 +} + +type Statfs_t struct { + Bsize uint32 + Iosize int32 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid Fsid + Owner uint32 + Type uint32 + Flags uint32 + Fssubtype uint32 + Fstypename [16]int8 + Mntonname [1024]int8 + Mntfromname [1024]int8 + Reserved [8]uint32 +} + +type Flock_t struct { + Start int64 + Len int64 + Pid int32 + Type int16 + Whence int16 +} + +type Fstore_t struct { + Flags uint32 + Posmode int32 + Offset int64 + Length int64 + Bytesalloc int64 +} + +type Radvisory_t struct { + Offset int64 + Count int32 +} + +type Fbootstraptransfer_t struct { + Offset int64 + Length uint32 + Buffer *byte +} + +type Log2phys_t struct { + Flags uint32 + Contigbytes int64 + Devoffset int64 +} + +type Fsid struct { + Val [2]int32 +} + +type Dirent struct { + Ino uint64 + Seekoff uint64 + Reclen uint16 + Namlen uint16 + Type uint8 + Name [1024]int8 + Pad_cgo_0 [3]byte +} + +type RawSockaddrInet4 struct { + Len uint8 + Family uint8 + Port uint16 + Addr [4]byte /* in_addr */ + Zero [8]int8 +} + +type RawSockaddrInet6 struct { + Len uint8 + Family uint8 + Port uint16 + Flowinfo uint32 + Addr [16]byte /* in6_addr */ + Scope_id uint32 +} + +type RawSockaddrUnix struct { + Len uint8 + Family uint8 + Path [104]int8 +} + +type RawSockaddrDatalink struct { + Len uint8 + Family uint8 + Index uint16 + Type uint8 + Nlen uint8 + Alen uint8 + Slen uint8 + Data [12]int8 +} + +type RawSockaddr struct { + Len uint8 + Family uint8 + Data [14]int8 +} + +type RawSockaddrAny struct { + Addr RawSockaddr + Pad [92]int8 +} + +type _Socklen uint32 + +type Linger struct { + Onoff int32 + Linger int32 +} + +type Iovec struct { + Base *byte + Len uint32 +} + +type IPMreq struct { + Multiaddr [4]byte /* in_addr */ + Interface [4]byte /* in_addr */ +} + +type IPv6Mreq struct { + Multiaddr [16]byte /* in6_addr */ + Interface uint32 +} + +type Msghdr struct { + Name *byte + Namelen uint32 + Iov *Iovec + Iovlen int32 + Control *byte + Controllen uint32 + Flags int32 +} + +type Cmsghdr struct { + Len uint32 + Level int32 + Type int32 +} + +type Inet4Pktinfo struct { + Ifindex uint32 + Spec_dst [4]byte /* in_addr */ + Addr [4]byte /* in_addr */ +} + +type Inet6Pktinfo struct { + Addr [16]byte /* in6_addr */ + Ifindex uint32 +} + +type IPv6MTUInfo struct { + Addr RawSockaddrInet6 + Mtu uint32 +} + +type ICMPv6Filter struct { + Filt [8]uint32 +} + +const ( + SizeofSockaddrInet4 = 0x10 + SizeofSockaddrInet6 = 0x1c + SizeofSockaddrAny = 0x6c + SizeofSockaddrUnix = 0x6a + SizeofSockaddrDatalink = 0x14 + SizeofLinger = 0x8 + SizeofIPMreq = 0x8 + SizeofIPv6Mreq = 0x14 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc + SizeofInet4Pktinfo = 0xc + SizeofInet6Pktinfo = 0x14 + SizeofIPv6MTUInfo = 0x20 + SizeofICMPv6Filter = 0x20 +) + +const ( + PTRACE_TRACEME = 0x0 + PTRACE_CONT = 0x7 + PTRACE_KILL = 0x8 +) + +type Kevent_t struct { + Ident uint32 + Filter int16 + Flags uint16 + Fflags uint32 + Data int32 + Udata *byte +} + +type FdSet struct { + Bits [32]int32 +} + +const ( + SizeofIfMsghdr = 0x70 + SizeofIfData = 0x60 + SizeofIfaMsghdr = 0x14 + SizeofIfmaMsghdr = 0x10 + SizeofIfmaMsghdr2 = 0x14 + SizeofRtMsghdr = 0x5c + SizeofRtMetrics = 0x38 +) + +type IfMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Data IfData +} + +type IfData struct { + Type uint8 + Typelen uint8 + Physical uint8 + Addrlen uint8 + Hdrlen uint8 + Recvquota uint8 + Xmitquota uint8 + Unused1 uint8 + Mtu uint32 + Metric uint32 + Baudrate uint32 + Ipackets uint32 + Ierrors uint32 + Opackets uint32 + Oerrors uint32 + Collisions uint32 + Ibytes uint32 + Obytes uint32 + Imcasts uint32 + Omcasts uint32 + Iqdrops uint32 + Noproto uint32 + Recvtiming uint32 + Xmittiming uint32 + Lastchange Timeval + Unused2 uint32 + Hwassist uint32 + Reserved1 uint32 + Reserved2 uint32 +} + +type IfaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Metric int32 +} + +type IfmaMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte +} + +type IfmaMsghdr2 struct { + Msglen uint16 + Version uint8 + Type uint8 + Addrs int32 + Flags int32 + Index uint16 + Pad_cgo_0 [2]byte + Refcount int32 +} + +type RtMsghdr struct { + Msglen uint16 + Version uint8 + Type uint8 + Index uint16 + Pad_cgo_0 [2]byte + Flags int32 + Addrs int32 + Pid int32 + Seq int32 + Errno int32 + Use int32 + Inits uint32 + Rmx RtMetrics +} + +type RtMetrics struct { + Locks uint32 + Mtu uint32 + Hopcount uint32 + Expire int32 + Recvpipe uint32 + Sendpipe uint32 + Ssthresh uint32 + Rtt uint32 + Rttvar uint32 + Pksent uint32 + Filler [4]uint32 +} + +const ( + SizeofBpfVersion = 0x4 + SizeofBpfStat = 0x8 + SizeofBpfProgram = 0x8 + SizeofBpfInsn = 0x8 + SizeofBpfHdr = 0x14 +) + +type BpfVersion struct { + Major uint16 + Minor uint16 +} + +type BpfStat struct { + Recv uint32 + Drop uint32 +} + +type BpfProgram struct { + Len uint32 + Insns *BpfInsn +} + +type BpfInsn struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type BpfHdr struct { + Tstamp Timeval + Caplen uint32 + Datalen uint32 + Hdrlen uint16 + Pad_cgo_0 [2]byte +} + +type Termios struct { + Iflag uint32 + Oflag uint32 + Cflag uint32 + Lflag uint32 + Cc [20]uint8 + Ispeed uint32 + Ospeed uint32 +} diff --git a/src/syscall/ztypes_windows.go b/src/syscall/ztypes_windows.go index 4c8a99ab94..e5c732576b 100644 --- a/src/syscall/ztypes_windows.go +++ b/src/syscall/ztypes_windows.go @@ -1083,12 +1083,7 @@ type TCPKeepalive struct { Interval uint32 } -type reparseDataBuffer struct { - ReparseTag uint32 - ReparseDataLength uint16 - Reserved uint16 - - // SymbolicLinkReparseBuffer +type symbolicLinkReparseBuffer struct { SubstituteNameOffset uint16 SubstituteNameLength uint16 PrintNameOffset uint16 @@ -1097,9 +1092,27 @@ type reparseDataBuffer struct { PathBuffer [1]uint16 } +type mountPointReparseBuffer struct { + SubstituteNameOffset uint16 + SubstituteNameLength uint16 + PrintNameOffset uint16 + PrintNameLength uint16 + PathBuffer [1]uint16 +} + +type reparseDataBuffer struct { + ReparseTag uint32 + ReparseDataLength uint16 + Reserved uint16 + + // GenericReparseBuffer + reparseBuffer byte +} + const ( FSCTL_GET_REPARSE_POINT = 0x900A8 MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 + _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 IO_REPARSE_TAG_SYMLINK = 0xA000000C SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 ) diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index ffd5376844..62e696d221 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -280,6 +280,14 @@ func (r BenchmarkResult) MemString() string { r.AllocedBytesPerOp(), r.AllocsPerOp()) } +// benchmarkName returns full name of benchmark including procs suffix. +func benchmarkName(name string, n int) string { + if n != 1 { + return fmt.Sprintf("%s-%d", name, n) + } + return name +} + // An internal function but exported because it is cross-package; part of the implementation // of the "go test" command. func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) { @@ -287,15 +295,30 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ if len(*matchBenchmarks) == 0 { return } + // Collect matching benchmarks and determine longest name. + maxprocs := 1 + for _, procs := range cpuList { + if procs > maxprocs { + maxprocs = procs + } + } + maxlen := 0 + var bs []InternalBenchmark for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { fmt.Fprintf(os.Stderr, "testing: invalid regexp for -test.bench: %s\n", err) os.Exit(1) } - if !matched { - continue + if matched { + bs = append(bs, Benchmark) + benchName := benchmarkName(Benchmark.Name, maxprocs) + if l := len(benchName); l > maxlen { + maxlen = l + } } + } + for _, Benchmark := range bs { for _, procs := range cpuList { runtime.GOMAXPROCS(procs) b := &B{ @@ -304,11 +327,8 @@ func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ }, benchmark: Benchmark, } - benchName := Benchmark.Name - if procs != 1 { - benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs) - } - fmt.Printf("%s\t", benchName) + benchName := benchmarkName(Benchmark.Name, procs) + fmt.Printf("%-*s\t", maxlen, benchName) r := b.run() if b.failed { // The output could be very long here, but probably isn't. diff --git a/src/time/sys_unix.go b/src/time/sys_unix.go index 379e13d6a5..e592415daa 100644 --- a/src/time/sys_unix.go +++ b/src/time/sys_unix.go @@ -74,3 +74,5 @@ func preadn(fd uintptr, buf []byte, off int) error { } return nil } + +func isNotExist(err error) bool { return err == syscall.ENOENT } diff --git a/src/time/zoneinfo_plan9.go b/src/time/zoneinfo_plan9.go index 4bb0cb3909..0694f0a990 100644 --- a/src/time/zoneinfo_plan9.go +++ b/src/time/zoneinfo_plan9.go @@ -7,7 +7,6 @@ package time import ( - "errors" "runtime" "syscall" ) @@ -148,11 +147,12 @@ func initLocal() { } func loadLocation(name string) (*Location, error) { - if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil { - z.name = name - return z, nil + z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name) + if err != nil { + return nil, err } - return nil, errors.New("unknown time zone " + name) + z.name = name + return z, nil } func forceZipFileForTesting(zipOnly bool) { diff --git a/src/time/zoneinfo_unix.go b/src/time/zoneinfo_unix.go index ab7e4612e4..66540969d5 100644 --- a/src/time/zoneinfo_unix.go +++ b/src/time/zoneinfo_unix.go @@ -74,11 +74,17 @@ func initLocal() { } func loadLocation(name string) (*Location, error) { + var firstErr error for _, zoneDir := range zoneDirs { if z, err := loadZoneFile(zoneDir, name); err == nil { z.name = name return z, nil + } else if firstErr == nil && !isNotExist(err) { + firstErr = err } } + if firstErr != nil { + return nil, firstErr + } return nil, errors.New("unknown time zone " + name) } diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go index 02d8e0edcc..5077f4bd86 100644 --- a/src/time/zoneinfo_windows.go +++ b/src/time/zoneinfo_windows.go @@ -260,11 +260,12 @@ func initLocal() { } func loadLocation(name string) (*Location, error) { - if z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name); err == nil { - z.name = name - return z, nil + z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name) + if err != nil { + return nil, err } - return nil, errors.New("unknown time zone " + name) + z.name = name + return z, nil } func forceZipFileForTesting(zipOnly bool) { |
