diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd/6g/cgen.c | 17 | ||||
-rw-r--r-- | src/cmd/6g/gen.c | 32 | ||||
-rw-r--r-- | src/cmd/6g/gg.h | 1 | ||||
-rw-r--r-- | src/cmd/6g/gsubr.c | 12 | ||||
-rw-r--r-- | src/cmd/gc/dcl.c | 3 | ||||
-rw-r--r-- | src/cmd/gc/go.h | 22 | ||||
-rw-r--r-- | src/cmd/gc/go.y | 3 | ||||
-rw-r--r-- | src/cmd/gc/subr.c | 19 | ||||
-rw-r--r-- | src/cmd/gc/walk.c | 249 | ||||
-rw-r--r-- | src/runtime/print.c | 10 | ||||
-rw-r--r-- | src/runtime/string.c | 31 |
11 files changed, 322 insertions, 77 deletions
diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 43497adb2..2774456c0 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -48,6 +48,11 @@ cgen(Node *n, Node *res) if(n->ullman > res->ullman) { regalloc(&n1, n->type, res); cgen(n, &n1); + if(n1.ullman > res->ullman) { + dump("n1", &n1); + dump("res", res); + fatal("loop in cgen"); + } cgen(&n1, res); regfree(&n1); goto ret; @@ -198,6 +203,7 @@ cgen(Node *n, Node *res) case ODOTPTR: case OINDEX: case OIND: + case ONAME: // PHEAP var igen(n, &n1, res); gmove(&n1, res); regfree(&n1); @@ -517,6 +523,17 @@ agen(Node *n, Node *res) regfree(&n3); break; + case ONAME: + // should only get here for heap vars + if(!(n->class & PHEAP)) + fatal("agen: bad ONAME class %#x", n->class); + cgen(n->heapaddr, res); + if(n->xoffset != 0) { + nodconst(&n1, types[TINT64], n->xoffset); + gins(optoas(OADD, types[tptr]), &n1, res); + } + break; + case OIND: cgen(nl, res); break; diff --git a/src/cmd/6g/gen.c b/src/cmd/6g/gen.c index f01f1d8b5..e6a685033 100644 --- a/src/cmd/6g/gen.c +++ b/src/cmd/6g/gen.c @@ -99,6 +99,7 @@ if(throwreturn == N) { // inarggen(); ginit(); + gen(curfn->enter, L); gen(curfn->nbody, L); gclean(); checklabels(); @@ -151,6 +152,8 @@ allocparams(void) dowidth(n->type); w = n->type->width; + if(n->class & PHEAP) + w = widthptr; stksize += w; stksize = rnd(stksize, w); @@ -345,6 +348,10 @@ loop: cgen_asop(n); break; + case ODCL: + cgen_dcl(n->left); + break; + case OAS: cgen_as(n->left, n->right); break; @@ -1115,6 +1122,26 @@ ret: } /* + * generate declaration. + * nothing to do for on-stack automatics, + * but might have to allocate heap copy + * for escaped variables. + */ +void +cgen_dcl(Node *n) +{ + if(debug['g']) + dump("\ncgen-dcl", n); + if(n->op != ONAME) { + dump("cgen_dcl", n); + fatal("cgen_dcl"); + } + if(!(n->class & PHEAP)) + return; + cgen_as(n->heapaddr, n->alloc); +} + +/* * generate assignment: * nl = nr * nr == N means zero nl. @@ -1130,6 +1157,11 @@ cgen_as(Node *nl, Node *nr) if(nl == N) return; + if(debug['g']) { + dump("cgen_as", nl); + dump("cgen_as = ", nr); + } + iszer = 0; if(nr == N || isnil(nr)) { if(nl->op == OLIST) { diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 881a23073..602de32b4 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -158,6 +158,7 @@ void cgen_callret(Node*, Node*); void cgen_div(int, Node*, Node*, Node*); void cgen_bmul(int, Node*, Node*, Node*); void cgen_shift(int, Node*, Node*, Node*); +void cgen_dcl(Node*); void genpanic(void); int needconvert(Type*, Type*); void genconv(Type*, Type*); diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 86ba52c3f..cd4f6e294 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -547,6 +547,7 @@ gmove(Node *f, Node *t) break; case PAUTO: case PPARAM: + case PPARAMOUT: break; } break; @@ -1046,6 +1047,15 @@ naddr(Node *n, Addr *a) 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->offset = n->xoffset; + a->sym = n->left->sym; + a->type = D_PARAM; + break; + case ONAME: a->etype = 0; if(n->type != T) @@ -1071,6 +1081,7 @@ naddr(Node *n, Addr *a) a->type = D_AUTO; break; case PPARAM: + case PPARAMOUT: a->type = D_PARAM; break; } @@ -1749,6 +1760,7 @@ tempname(Node *n, Type *t) n->class = PAUTO; n->addable = 1; n->ullman = 1; + n->noescape = 1; dowidth(t); w = t->width; diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index edac4ca2c..2ae8fd308 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -36,6 +36,7 @@ dodclvar(Node *n, Type *t) addvar(n, t, dclcontext); autoexport(n->sym); + addtop = list(addtop, nod(ODCL, n, N)); } void @@ -434,7 +435,7 @@ funcargs(Type *ft) if(t->nname != N) t->nname->xoffset = t->width; if(t->nname != N) { - addvar(t->nname, t->type, PPARAM); + addvar(t->nname, t->type, PPARAMOUT); all |= 1; } else all |= 2; diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index ba2672390..1e1f4b28f 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -180,12 +180,13 @@ struct Node uchar addable; // type of addressability - 0 is not addressable uchar trecur; // to detect loops uchar etype; // op for OASOP, etype for OTYPE, exclam for export - uchar class; // PPARAM, PAUTO, PEXTERN + uchar class; // PPARAM, PAUTO, PEXTERN, etc uchar method; // OCALLMETH name uchar iota; // OLITERAL made from iota uchar embedded; // ODCLFIELD embedded type uchar colas; // OAS resulting from := uchar diag; // already printed error about this + uchar noescape; // ONAME never move to heap // most nodes Node* left; @@ -206,10 +207,17 @@ struct Node // func Node* nname; + Node* enter; + Node* exit; // OLITERAL/OREGISTER Val val; + // ONAME func param with PHEAP + Node* heapaddr; // temp holding heap address of param + Node* stackparam; // OPARAM node referring to stack copy of param + Node* alloc; // allocation call + Sym* osym; // import Sym* psym; // import Sym* sym; // various @@ -287,7 +295,7 @@ enum OTYPE, OCONST, OVAR, OIMPORT, - ONAME, ONONAME, + ONAME, ONONAME, ODCL, ODOT, ODOTPTR, ODOTMETH, ODOTINTER, ODCLFUNC, ODCLFIELD, ODCLARG, OLIST, OCMP, OPTR, OARRAY, ORANGE, @@ -312,7 +320,7 @@ enum OINDEX, OSLICE, ONOT, OCOM, OPLUS, OMINUS, OSEND, ORECV, OLITERAL, OREGISTER, OINDREG, - OCONV, OCOMP, OKEY, + OCONV, OCOMP, OKEY, OPARAM, OBAD, OEXTEND, // 6g internal @@ -405,6 +413,9 @@ enum PEXTERN, // declaration context PAUTO, PPARAM, + PPARAMOUT, + + PHEAP = 1<<7, }; enum @@ -654,6 +665,7 @@ Node* treecopy(Node*); int isselect(Node*); void tempname(Node*, Type*); int iscomposite(Type*); +Node* callnew(Type*); Type** getthis(Type*); Type** getoutarg(Type*); @@ -812,11 +824,13 @@ Node* reorder1(Node*); Node* reorder2(Node*); Node* reorder3(Node*); Node* reorder4(Node*); -Node* structlit(Node*); +Node* structlit(Node*, Node*); Node* arraylit(Node*); Node* maplit(Node*); Node* selectas(Node*, Node*); Node* old2new(Node*, Type*); +void addrescapes(Node*); +void heapmoves(void); /* * const.c diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index d04991dc4..29c9b29d9 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -278,6 +278,7 @@ Avardcl: dodclvar($$, $2); $$ = nod(OAS, $$, N); + addtotop($$); } Bvardcl: @@ -287,6 +288,7 @@ Bvardcl: dodclvar($$, $2); $$ = nod(OAS, $$, N); + addtotop($$); } | new_name_list_r type '=' expr_list { @@ -478,6 +480,7 @@ complex_stmt: poptodcl(); $$ = nod(OAS, selectas($2,$4), $4); $$ = nod(OXCASE, $$, N); + addtotop($$); } | LDEFAULT ':' { diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 6cd038412..d6fb25147 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -702,6 +702,8 @@ opnames[] = [OPANICN] = "PANICN", [OPRINT] = "PRINT", [OPRINTN] = "PRINTN", + [OPARAM] = "PARAM", + [ODCL] = "DCL", [OXXX] = "XXX", }; @@ -877,6 +879,16 @@ Jconv(Fmt *fp) strncat(buf, buf1, sizeof(buf)); } + if(n->class != 0) { + snprint(buf1, sizeof(buf1), " class(%d)", n->class); + strncat(buf, buf1, sizeof(buf)); + } + + if(n->colas != 0) { + snprint(buf1, sizeof(buf1), " colas(%d)", n->colas); + strncat(buf, buf1, sizeof(buf)); + } + return fmtstrcpy(fp, buf); } @@ -2031,6 +2043,7 @@ ullmancalc(Node *n) return; switch(n->op) { + case OREGISTER: case OLITERAL: case ONAME: ul = 1; @@ -2281,7 +2294,7 @@ Type** getthis(Type *t) { if(t->etype != TFUNC) - fatal("getthis: not a func %N", t); + fatal("getthis: not a func %T", t); return &t->type; } @@ -2289,7 +2302,7 @@ Type** getoutarg(Type *t) { if(t->etype != TFUNC) - fatal("getoutarg: not a func %N", t); + fatal("getoutarg: not a func %T", t); return &t->type->down; } @@ -2297,7 +2310,7 @@ Type** getinarg(Type *t) { if(t->etype != TFUNC) - fatal("getinarg: not a func %N", t); + fatal("getinarg: not a func %T", t); return &t->type->down->down; } diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 780da1433..1b5ca4746 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -65,13 +65,20 @@ walk(Node *fn) if(curfn->type->outtuple) if(walkret(curfn->nbody)) yyerror("function ends without a return statement"); - if(addtop != N) + if(addtop != N) { + dump("addtop", addtop); fatal("addtop in walk"); + } walkstate(curfn->nbody); if(debug['W']) { - snprint(s, sizeof(s), "after %S", curfn->nname->sym); + snprint(s, sizeof(s), "after walk %S", curfn->nname->sym); dump(s, curfn->nbody); } + heapmoves(); + if(debug['W'] && curfn->enter != N) { + snprint(s, sizeof(s), "enter %S", curfn->nname->sym); + dump(s, curfn->enter); + } } void @@ -125,6 +132,7 @@ loop: case OCALLMETH: case OCALLINTER: case OCALL: + case ODCL: case OSEND: case ORECV: case OPRINT: @@ -229,6 +237,9 @@ loop: fatal("walktype: switch 1 unknown op %N", n); goto ret; + case ODCL: + goto ret; + case OLIST: case OKEY: walktype(n->left, top); @@ -283,7 +294,8 @@ loop: case ONAME: if(top == Etop) goto nottop; - n->addable = 1; + if(!(n->class & PHEAP)) + n->addable = 1; if(n->type == T) { s = n->sym; if(s->undef == 0) { @@ -681,7 +693,7 @@ loop: // structure literal if(t->etype == TSTRUCT) { - indir(n, structlit(n)); + indir(n, structlit(n, nil)); goto ret; } @@ -996,32 +1008,20 @@ loop: // nvar := new(*Point); // *nvar = Point{1, 2}; // and replace expression with nvar + Node *nvar, *nas; - // TODO(rsc): might do a better job (fewer copies) later - Node *nnew, *nvar, *nas; - - t = ptrto(n->left->type); - walktype(n->left, Elv); - if(n->left == N) - goto ret; - - nvar = nod(0, N, N); - tempname(nvar, t); - - nnew = nod(ONEW, N, N); - nnew->type = n->left->type; - nnew = newcompat(nnew); + nvar = nod(OXXX, N, N); + tempname(nvar, ptrto(n->left->type)); - nas = nod(OAS, nvar, nnew); - addtop = list(addtop, nas); - - nas = nod(OAS, nod(OIND, nvar, N), n->left); + nas = nod(OAS, nvar, callnew(n->left->type)); addtop = list(addtop, nas); + structlit(n->left, nvar); indir(n, nvar); goto ret; } walktype(n->left, Elv); + addrescapes(n->left); if(n->left == N) goto ret; t = n->left->type; @@ -1055,7 +1055,16 @@ loop: case ONEW: if(top != Erv) goto nottop; - indir(n, newcompat(n)); + if(n->left != N) { + yyerror("cannot new(%T, expr)", t); + goto ret; + } + t = n->type; + if(t == T || t->etype == TFUNC) { + yyerror("cannot new(%T)", t); + goto ret; + } + indir(n, callnew(t)); goto ret; } @@ -1455,8 +1464,8 @@ void walkselect(Node *sel) { Iter iter; - Node *n, *oc, *on, *r; - Node *var, *bod, *res, *def; + Node *n, *l, *oc, *on, *r; + Node *var, *bod, *nbod, *res, *def; int count, op; int32 lno; @@ -1492,6 +1501,7 @@ walkselect(Node *sel) def = n; } else op = n->left->op; + nbod = N; switch(op) { default: yyerror("select cases must be send, recv or default"); @@ -1499,14 +1509,32 @@ walkselect(Node *sel) case OAS: // convert new syntax (a=recv(chan)) to (recv(a,chan)) - if(n->left->right == N || n->left->right->op != ORECV) { + l = n->left; + if(l->right == N || l->right->op != ORECV) { yyerror("select cases must be send, recv or default"); break; } - n->left->right->right = n->left->right->left; - n->left->right->left = n->left->left; - n->left = n->left->right; + r = l->right; // rcv + r->right = r->left; + r->left = l->left; + n->left = r; + + // convert case x := foo: body + // to case tmp := foo: x := tmp; body. + // if x escapes and must be allocated + // on the heap, this delays the allocation + // until after the select has chosen this branch. + if(n->ninit != N && n->ninit->op == ODCL) { + on = nod(OXXX, N, N); + tempname(on, l->left->type); + on->sym = lookup("!tmpselect!"); + r->left = on; + nbod = nod(OAS, l->left, on); + nbod->ninit = n->ninit; + n->ninit = N; + } + // fall through case OSEND: case ORECV: if(oc != N) { @@ -1516,10 +1544,8 @@ walkselect(Node *sel) oc = selcase(n, var); res = list(res, oc); break; - - } - bod = N; + bod = nbod; count++; break; } @@ -1610,6 +1636,7 @@ lookdot(Node *n, Type *t) switch(op) { case OADDR: walktype(n->left, Elv); + addrescapes(n->left); n->left = nod(OADDR, n->left, N); n->left->type = ptrto(tt); break; @@ -1781,7 +1808,7 @@ sigtype(Type *st) * match a ... parameter into an * automatic structure. * then call the ... arg (interface) - * with a pointer to the structure + * with a pointer to the structure. */ Node* mkdotargs(Node *r, Node *rr, Iter *saver, Node *nn, Type *l, int fp) @@ -1817,10 +1844,12 @@ mkdotargs(Node *r, Node *rr, Iter *saver, Node *nn, Type *l, int fp) // make a named type for the struct st = sigtype(st); + dowidth(st); // now we have the size, make the struct var = nod(OXXX, N, N); tempname(var, st); + var->sym = lookup(".ddd"); // assign the fields to the struct. // use addtop so that reorder1 doesn't reorder @@ -1840,8 +1869,7 @@ mkdotargs(Node *r, Node *rr, Iter *saver, Node *nn, Type *l, int fp) } // last thing is to put assignment - // of a pointer to the structure to - // the DDD parameter + // of the structure to the DDD parameter a = nod(OAS, nodarg(l, fp), var); nn = list(convas(a), nn); @@ -2081,26 +2109,17 @@ makecompat(Node *n) } Node* -newcompat(Node *n) +callnew(Type *t) { Node *r, *on; - Type *t; - t = n->type; - if(t != T && t->etype != TFUNC) { - if(n->left != N) - yyerror("cannot new(%T, expr)", t); - dowidth(t); - on = syslook("mal", 1); - argtype(on, t); - r = nodintconst(t->width); - r = nod(OCALL, on, r); - walktype(r, Erv); - return r; - } - - yyerror("cannot new(%T)", t); - return n; + dowidth(t); + on = syslook("mal", 1); + argtype(on, t); + r = nodintconst(t->width); + r = nod(OCALL, on, r); + walktype(r, Erv); + return r; } Node* @@ -2648,6 +2667,7 @@ arrayop(Node *n, int top) r = a; a = nod(OADDR, n->left, N); // old + addrescapes(n->left); r = list(a, r); on = syslook("arrays2d", 1); @@ -2671,6 +2691,7 @@ arrayop(Node *n, int top) r = a; a = nod(OADDR, n->right, N); // old + addrescapes(n->right); r = list(a, r); on = syslook("arrays2d", 1); @@ -3171,6 +3192,7 @@ ary: n->nbody = list(n->nbody, nod(OAS, v, nod(OINDEX, m, hk)) ); } + addtotop(n); goto out; map: @@ -3457,18 +3479,20 @@ reorder4(Node *n) } Node* -structlit(Node *n) +structlit(Node *n, Node *var) { Iter savel, saver; Type *l, *t; - Node *var, *r, *a; + Node *r, *a; t = n->type; if(t->etype != TSTRUCT) fatal("structlit: not struct"); - var = nod(OXXX, N, N); - tempname(var, t); + if(var == N) { + var = nod(OXXX, N, N); + tempname(var, t); + } l = structfirst(&savel, &n->type); r = listfirst(&saver, &n->left); @@ -3488,6 +3512,7 @@ loop: a = nod(ODOT, var, newname(l->sym)); a = nod(OAS, a, r); + walktype(a, Etop); // add any assignments in r to addtop addtop = list(addtop, a); l = structnext(&savel); @@ -3605,3 +3630,115 @@ loop: r = listnext(&saver); goto loop; } + +/* + * the address of n has been taken and might be used after + * the current function returns. mark any local vars + * as needing to move to the heap. + */ +static char *pnames[] = { +[PAUTO] "auto", +[PPARAM] "param", +[PPARAMOUT] "param_out", +}; + +void +addrescapes(Node *n) +{ + char buf[100]; + switch(n->op) { + default: + dump("addrescapes", n); + break; + + case ONAME: + if(n->noescape) + break; + switch(n->class) { + case PPARAMOUT: + yyerror("cannot take address of out parameter %s", n->sym->name); + break; + case PAUTO: + case PPARAM: + if(debug['E']) + print("%L %s %S escapes %p\n", n->lineno, pnames[n->class], n->sym, n); + n->class |= PHEAP; + n->addable = 0; + n->ullman = 2; + n->alloc = callnew(n->type); + + // if func param, need separate temporary + // to hold heap pointer. + if(n->class == PPARAM+PHEAP) { + // expression to refer to stack copy + n->stackparam = nod(OPARAM, n, N); + n->stackparam->type = n->type; + n->stackparam->addable = 1; + n->stackparam->xoffset = n->xoffset; + n->xoffset = 0; + } + + // create stack variable to hold pointer to heap + n->heapaddr = nod(0, N, N); + tempname(n->heapaddr, ptrto(n->type)); + snprint(buf, sizeof buf, "&%S", n->sym); + n->heapaddr->sym = lookup(buf); + break; + } + break; + + case OIND: + case ODOTPTR: + break; + + case ODOT: + case OINDEX: + // ODOTPTR has already been + // introduced, so these are the non-pointer + // ODOT and OINDEX. + addrescapes(n->left); + break; + } +} + +/* + * walk through argin parameters. + * generate and return code to allocate + * copies of escaped parameters to the heap. + */ +Node* +paramstoheap(Type **argin) +{ + Type *t; + Iter savet; + Node *v, *nn; + + nn = N; + for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { + if(t->sym == S) + continue; + v = t->sym->oname; + if(v == N || !(v->class & PHEAP)) + continue; + + // generate allocation & copying code + nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); + nn = list(nn, nod(OAS, v, v->stackparam)); + } + return nn; +} + +/* + * take care of migrating any function in/out args + * between the stack and the heap. adds code to + * curfn's before and after lists. + */ +void +heapmoves(void) +{ + Node *nn; + + nn = paramstoheap(getthis(curfn->type)); + nn = list(nn, paramstoheap(getinarg(curfn->type))); + curfn->enter = list(curfn->enter, nn); +} diff --git a/src/runtime/print.c b/src/runtime/print.c index 6b0000e21..bdd9abc95 100644 --- a/src/runtime/print.c +++ b/src/runtime/print.c @@ -237,8 +237,14 @@ sys·printpointer(void *p) void sys·printstring(string v) { - if(v != nil) - sys·write(1, v->str, v->len); + extern int32 maxstring; + + if(v != nil) { + if(v->len > maxstring) + sys·write(1, "[invalid string]", 16); + else + sys·write(1, v->str, v->len); + } } void diff --git a/src/runtime/string.c b/src/runtime/string.c index b31e7cc78..e708d0203 100644 --- a/src/runtime/string.c +++ b/src/runtime/string.c @@ -17,6 +17,20 @@ findnull(byte *s) return l; } +int32 maxstring; + +string +gostringsize(int32 l) +{ + string s; + + s = mal(sizeof(s->len)+l+1); + s->len = l; + if(l > maxstring) + maxstring = l; + return s; +} + string gostring(byte *str) { @@ -24,8 +38,7 @@ gostring(byte *str) string s; l = findnull(str); - s = mal(sizeof(s->len)+l+1); - s->len = l; + s = gostringsize(l); mcpy(s->str, str, l+1); return s; } @@ -46,8 +59,7 @@ sys·catstring(string s1, string s2, string s3) l = s1->len + s2->len; - s3 = mal(sizeof(s3->len)+l); - s3->len = l; + s3 = gostringsize(l); mcpy(s3->str, s1->str, s1->len); mcpy(s3->str+s1->len, s2->str, s2->len); @@ -139,8 +151,7 @@ sys·slicestring(string si, int32 lindex, int32 hindex, string so) } l = hindex-lindex; - so = mal(sizeof(so->len)+l); - so->len = l; + so = gostringsize(l); mcpy(so->str, si->str+lindex, l); FLUSH(&so); } @@ -164,7 +175,7 @@ sys·indexstring(string s, int32 i, byte b) void sys·intstring(int64 v, string s) { - s = mal(sizeof(s->len)+8); + s = gostringsize(8); s->len = runetochar(s->str, v); FLUSH(&s); } @@ -172,8 +183,7 @@ sys·intstring(int64 v, string s) void sys·byteastring(byte *a, int32 l, string s) { - s = mal(sizeof(s->len)+l); - s->len = l; + s = gostringsize(l); mcpy(s->str, a, l); FLUSH(&s); } @@ -181,8 +191,7 @@ sys·byteastring(byte *a, int32 l, string s) void sys·arraystring(Array b, string s) { - s = mal(sizeof(s->len)+b.nel); - s->len = b.nel; + s = gostringsize(b.nel); mcpy(s->str, b.array, s->len); FLUSH(&s); } |