From: Austin Clements Date: Wed, 22 Oct 2014 15:21:16 +0000 (-0400) Subject: [dev.power64] build: merge default into dev.power64 X-Git-Tag: go1.5beta1~2684^2~25^2~18 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=32082501859c5c3c61913df5791cafbbb45c2519;p=gostls13.git [dev.power64] build: merge default into dev.power64 LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/160200044 --- 32082501859c5c3c61913df5791cafbbb45c2519 diff --cc lib/codereview/codereview.cfg index 2801ebf8d6,2801ebf8d6..43dbf3ce3b --- a/lib/codereview/codereview.cfg +++ b/lib/codereview/codereview.cfg @@@ -1,1 -1,1 +1,2 @@@ defaultcc: golang-codereviews@googlegroups.com ++contributors: http://go.googlecode.com/hg/CONTRIBUTORS diff --cc lib/codereview/codereview.py index fdf11d1f48,fdf11d1f48..263385b79f --- a/lib/codereview/codereview.py +++ b/lib/codereview/codereview.py @@@ -3603,11 -3603,11 +3603,17 @@@ class MercurialVCS(VersionControlSystem if use_hg_shell: base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], silent_ok=True) else: -- base_content = str(self.repo[base_rev][oldrelpath].data()) ++ try: ++ base_content = str(self.repo[base_rev][oldrelpath].data()) ++ except Exception: ++ pass is_binary = "\0" in base_content # Mercurial's heuristic if status != "R": -- new_content = open(relpath, "rb").read() -- is_binary = is_binary or "\0" in new_content ++ try: ++ new_content = open(relpath, "rb").read() ++ is_binary = is_binary or "\0" in new_content ++ except Exception: ++ pass if is_binary and base_content and use_hg_shell: # Fetch again without converting newlines base_content = RunShell(["hg", "cat", "-r", base_rev, oldrelpath], diff --cc src/cmd/9c/cgen.c index 0d7a402945,0000000000..aeedc60c06 mode 100644,000000..100644 --- a/src/cmd/9c/cgen.c +++ b/src/cmd/9c/cgen.c @@@ -1,1135 -1,0 +1,1151 @@@ +// cmd/9c/cgen.c from Vita Nuova. +// +// 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-2008 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-2008 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 "gc.h" +#include "../../pkg/runtime/funcdata.h" + +void +cgen(Node *n, Node *nn) +{ + Node *l, *r; + Prog *p1; + Node nod, nod1, nod2, nod3, nod4; + int o; + int32 v, curs; + + if(debug['g']) { + prtree(nn, "cgen lhs"); + prtree(n, "cgen"); + } + if(n == Z || n->type == T) + return; - if(typesu[n->type->etype]) { ++ if(typesu[n->type->etype] && (n->op != OFUNC || nn != Z)) { + sugen(n, nn, n->type->width); + return; + } + l = n->left; + r = n->right; + o = n->op; + if(n->addable >= INDEXED) { + if(nn == Z) { + switch(o) { + default: + nullwarn(Z, Z); + break; + case OINDEX: + nullwarn(l, r); + break; + } + return; + } + gmove(n, nn); + return; + } + curs = cursafe; + + if(n->complex >= FNX) + if(l->complex >= FNX) + if(r != Z && r->complex >= FNX) + switch(o) { + default: - regret(&nod, r); ++ regret(&nod, r, 0, 0); + cgen(r, &nod); + + regsalloc(&nod1, r); + gopcode(OAS, &nod, Z, &nod1); + + regfree(&nod); + nod = *n; + nod.right = &nod1; + cgen(&nod, nn); + return; + + case OFUNC: + case OCOMMA: + case OANDAND: + case OOROR: + case OCOND: + case ODOT: + break; + } + + switch(o) { + default: + diag(n, "unknown op in cgen: %O", o); + break; + + case OAS: + if(l->op == OBIT) + goto bitas; + if(l->addable >= INDEXED) { + if(nn != Z || r->addable < INDEXED) { + regalloc(&nod, r, nn); + cgen(r, &nod); + gmove(&nod, l); + regfree(&nod); + } else + gmove(r, l); + break; + } + if(l->complex >= r->complex) { + reglcgen(&nod1, l, Z); + if(r->addable >= INDEXED) { + gmove(r, &nod1); + if(nn != Z) + gmove(r, nn); + regfree(&nod1); + break; + } + regalloc(&nod, r, nn); + cgen(r, &nod); + } else { + regalloc(&nod, r, nn); + cgen(r, &nod); + reglcgen(&nod1, l, Z); + } + gmove(&nod, &nod1); + regfree(&nod); + regfree(&nod1); + break; + + bitas: + n = l->left; + regalloc(&nod, r, nn); + if(l->complex >= r->complex) { + reglcgen(&nod1, n, Z); + cgen(r, &nod); + } else { + cgen(r, &nod); + reglcgen(&nod1, n, Z); + } + regalloc(&nod2, n, Z); + gopcode(OAS, &nod1, Z, &nod2); + bitstore(l, &nod, &nod1, &nod2, nn); + break; + + case OBIT: + if(nn == Z) { + nullwarn(l, Z); + break; + } + bitload(n, &nod, Z, Z, nn); + gopcode(OAS, &nod, Z, nn); + regfree(&nod); + break; + + case OXOR: + if(nn != Z) + if(r->op == OCONST && r->vconst == -1){ + cgen(l, nn); + gopcode(OCOM, nn, Z, nn); + break; + } + + case OADD: + case OSUB: + case OAND: + case OOR: + case OLSHR: + case OASHL: + case OASHR: + /* + * immediate operands + */ + if(nn != Z && + r->op == OCONST && + !typefd[n->type->etype] && + immconst(r)) { + cgen(l, nn); + if(r->vconst == 0) + if(o != OAND) + break; + if(nn != Z) + gopcode(o, r, Z, nn); + break; + } + + case OMUL: + case OLMUL: + case OLDIV: + case OLMOD: + case ODIV: + case OMOD: + if(nn == Z) { + nullwarn(l, r); + break; + } + if(o == OMUL || o == OLMUL) { + if(mulcon(n, nn)) + break; + if(debug['M']) + print("%L multiply\n", n->lineno); + } + if(l->complex >= r->complex) { + regalloc(&nod, l, nn); + cgen(l, &nod); + regalloc(&nod1, l, Z); /* note: l used for type, so shifts work! */ + cgen(r, &nod1); + gopcode(o, &nod1, Z, &nod); + } else { + regalloc(&nod, l, nn); /* note: l used for type, so shifts work! */ + cgen(r, &nod); + regalloc(&nod1, l, Z); + cgen(l, &nod1); + gopcode(o, &nod, &nod1, &nod); + } + gopcode(OAS, &nod, Z, nn); + regfree(&nod); + regfree(&nod1); + break; + + case OASLSHR: + case OASASHL: + case OASASHR: + case OASAND: + case OASADD: + case OASSUB: + case OASXOR: + case OASOR: + if(l->op == OBIT) + goto asbitop; + if(r->op == OCONST && + !typefd[n->type->etype] && + immconst(r)) { + if(l->addable < INDEXED) + reglcgen(&nod2, l, Z); + else + nod2 = *l; + regalloc(&nod, l, nn); /* note: l used for type, so shifts work! */ + gopcode(OAS, &nod2, Z, &nod); + gopcode(o, r, Z, &nod); + gopcode(OAS, &nod, Z, &nod2); + + regfree(&nod); + if(l->addable < INDEXED) + regfree(&nod2); + break; + } + + case OASLMUL: + case OASLDIV: + case OASLMOD: + case OASMUL: + case OASDIV: + case OASMOD: + if(l->op == OBIT) + goto asbitop; + if(l->complex >= r->complex) { + if(l->addable < INDEXED) + reglcgen(&nod2, l, Z); + else + nod2 = *l; + regalloc(&nod, n, nn); + cgen(r, &nod); + } else { + regalloc(&nod, n, nn); + cgen(r, &nod); + if(l->addable < INDEXED) + reglcgen(&nod2, l, Z); + else + nod2 = *l; + } + regalloc(&nod1, n, Z); + gopcode(OAS, &nod2, Z, &nod1); + if(nod1.type->etype != nod.type->etype){ + regalloc(&nod3, &nod, Z); + gmove(&nod1, &nod3); + regfree(&nod1); + nod1 = nod3; + } + gopcode(o, &nod, &nod1, &nod); + gmove(&nod, &nod2); + if(nn != Z) + gmove(&nod, nn); + regfree(&nod); + regfree(&nod1); + if(l->addable < INDEXED) + regfree(&nod2); + break; + + asbitop: + regalloc(&nod4, n, nn); + regalloc(&nod3, r, Z); + if(l->complex >= r->complex) { + bitload(l, &nod, &nod1, &nod2, &nod4); + cgen(r, &nod3); + } else { + cgen(r, &nod3); + bitload(l, &nod, &nod1, &nod2, &nod4); + } + gmove(&nod, &nod4); + gopcode(n->op, &nod3, Z, &nod4); + regfree(&nod3); + gmove(&nod4, &nod); + regfree(&nod4); + bitstore(l, &nod, &nod1, &nod2, nn); + break; + + case OADDR: + if(nn == Z) { + nullwarn(l, Z); + break; + } + lcgen(l, nn); + break; + + case OFUNC: + if(l->complex >= FNX) { + if(l->op != OIND) + diag(n, "bad function call"); + - regret(&nod, l->left); ++ regret(&nod, l->left, 0, 0); + cgen(l->left, &nod); + regsalloc(&nod1, l->left); + gopcode(OAS, &nod, Z, &nod1); + regfree(&nod); + + nod = *n; + nod.left = &nod2; + nod2 = *l; + nod2.left = &nod1; + nod2.complex = 1; + cgen(&nod, nn); + + return; + } + if(REGARG >= 0) + o = reg[REGARG]; + gargs(r, &nod, &nod1); + gpcdata(PCDATA_ArgSize, curarg); + if(l->addable < INDEXED) { + reglcgen(&nod, l, Z); + gopcode(OFUNC, Z, Z, &nod); + regfree(&nod); + } else + gopcode(OFUNC, Z, Z, l); + gpcdata(PCDATA_ArgSize, -1); + if(REGARG>=0) + if(o != reg[REGARG]) + reg[REGARG]--; - if(nn != Z) { - regret(&nod, n); ++ regret(&nod, n, l->type, 1); // update maxarg if nothing else ++ gpcdata(PCDATA_ArgSize, curarg); ++ gpcdata(PCDATA_ArgSize, -1); ++ if(nn != Z) + gopcode(OAS, &nod, Z, nn); ++ if(nod.op == OREGISTER) + regfree(&nod); - } + break; + + case OIND: + if(nn == Z) { + cgen(l, nn); + break; + } + regialloc(&nod, n, nn); + r = l; + while(r->op == OADD) + r = r->right; + if(sconst(r)) { + v = r->vconst; + r->vconst = 0; + cgen(l, &nod); + nod.xoffset += v; + r->vconst = v; + } else + cgen(l, &nod); + regind(&nod, n); + gopcode(OAS, &nod, Z, nn); + regfree(&nod); + break; + + case OEQ: + case ONE: + case OLE: + case OLT: + case OGE: + case OGT: + case OLO: + case OLS: + case OHI: + case OHS: + if(nn == Z) { + nullwarn(l, r); + break; + } + boolgen(n, 1, nn); + break; + + case OANDAND: + case OOROR: + boolgen(n, 1, nn); + if(nn == Z) + patch(p, pc); + break; + + case ONOT: + if(nn == Z) { + nullwarn(l, Z); + break; + } + boolgen(n, 1, nn); + break; + + case OCOMMA: + cgen(l, Z); + cgen(r, nn); + break; + + case OCAST: + if(nn == Z) { + nullwarn(l, Z); + break; + } + /* + * convert from types l->n->nn + */ + if(nocast(l->type, n->type) && nocast(n->type, nn->type)) { + /* both null, gen l->nn */ + cgen(l, nn); + break; + } + regalloc(&nod, l, nn); + cgen(l, &nod); + regalloc(&nod1, n, &nod); + gopcode(OAS, &nod, Z, &nod1); + gopcode(OAS, &nod1, Z, nn); + regfree(&nod1); + regfree(&nod); + break; + + case ODOT: + sugen(l, nodrat, l->type->width); + if(nn != Z) { + warn(n, "non-interruptable temporary"); + nod = *nodrat; + if(!r || r->op != OCONST) { + diag(n, "DOT and no offset"); + break; + } + nod.xoffset += (int32)r->vconst; + nod.type = n->type; + cgen(&nod, nn); + } + break; + + case OCOND: + bcgen(l, 1); + p1 = p; + cgen(r->left, nn); + gbranch(OGOTO); + patch(p1, pc); + p1 = p; + cgen(r->right, nn); + patch(p1, pc); + break; + + case OPOSTINC: + case OPOSTDEC: + v = 1; + if(l->type->etype == TIND) + v = l->type->link->width; + if(o == OPOSTDEC) + v = -v; + if(l->op == OBIT) + goto bitinc; + if(nn == Z) + goto pre; + + if(l->addable < INDEXED) + reglcgen(&nod2, l, Z); + else + nod2 = *l; + + regalloc(&nod, l, nn); + gopcode(OAS, &nod2, Z, &nod); + regalloc(&nod1, l, Z); + if(typefd[l->type->etype]) { + regalloc(&nod3, l, Z); + if(v < 0) { + gopcode(OAS, nodfconst(-v), Z, &nod3); + gopcode(OSUB, &nod3, &nod, &nod1); + } else { + gopcode(OAS, nodfconst(v), Z, &nod3); + gopcode(OADD, &nod3, &nod, &nod1); + } + regfree(&nod3); + } else + gopcode(OADD, nodconst(v), &nod, &nod1); + gopcode(OAS, &nod1, Z, &nod2); + + regfree(&nod); + regfree(&nod1); + if(l->addable < INDEXED) + regfree(&nod2); + break; + + case OPREINC: + case OPREDEC: + v = 1; + if(l->type->etype == TIND) + v = l->type->link->width; + if(o == OPREDEC) + v = -v; + if(l->op == OBIT) + goto bitinc; + + pre: + if(l->addable < INDEXED) + reglcgen(&nod2, l, Z); + else + nod2 = *l; + + regalloc(&nod, l, nn); + gopcode(OAS, &nod2, Z, &nod); + if(typefd[l->type->etype]) { + regalloc(&nod3, l, Z); + if(v < 0) { + gopcode(OAS, nodfconst(-v), Z, &nod3); + gopcode(OSUB, &nod3, Z, &nod); + } else { + gopcode(OAS, nodfconst(v), Z, &nod3); + gopcode(OADD, &nod3, Z, &nod); + } + regfree(&nod3); + } else + gopcode(OADD, nodconst(v), Z, &nod); + gopcode(OAS, &nod, Z, &nod2); + if(nn && l->op == ONAME) /* in x=++i, emit USED(i) */ + gins(ANOP, l, Z); + + regfree(&nod); + if(l->addable < INDEXED) + regfree(&nod2); + break; + + bitinc: + if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) { + bitload(l, &nod, &nod1, &nod2, Z); + gopcode(OAS, &nod, Z, nn); + gopcode(OADD, nodconst(v), Z, &nod); + bitstore(l, &nod, &nod1, &nod2, Z); + break; + } + bitload(l, &nod, &nod1, &nod2, nn); + gopcode(OADD, nodconst(v), Z, &nod); + bitstore(l, &nod, &nod1, &nod2, nn); + break; + } + cursafe = curs; +} + +void +reglcgen(Node *t, Node *n, Node *nn) +{ + Node *r; + int32 v; + + regialloc(t, n, nn); + if(n->op == OIND) { + r = n->left; + while(r->op == OADD) + r = r->right; + if(sconst(r)) { + v = r->vconst; + r->vconst = 0; + lcgen(n, t); + t->xoffset += v; + r->vconst = v; + regind(t, n); + return; + } + } + lcgen(n, t); + regind(t, n); +} + +void +lcgen(Node *n, Node *nn) +{ + Prog *p1; + Node nod; + + if(debug['g']) { + prtree(nn, "lcgen lhs"); + prtree(n, "lcgen"); + } + if(n == Z || n->type == T) + return; + if(nn == Z) { + nn = &nod; + regalloc(&nod, n, Z); + } + switch(n->op) { + default: + if(n->addable < INDEXED) { + diag(n, "unknown op in lcgen: %O", n->op); + break; + } + nod = *n; + nod.op = OADDR; + nod.left = n; + nod.right = Z; + nod.type = types[TIND]; + gopcode(OAS, &nod, Z, nn); + break; + + case OCOMMA: + cgen(n->left, n->left); + lcgen(n->right, nn); + break; + + case OIND: + cgen(n->left, nn); + break; + + case OCOND: + bcgen(n->left, 1); + p1 = p; + lcgen(n->right->left, nn); + gbranch(OGOTO); + patch(p1, pc); + p1 = p; + lcgen(n->right->right, nn); + patch(p1, pc); + break; + } +} + +void +bcgen(Node *n, int true) +{ + + if(n->type == T) + gbranch(OGOTO); + else + boolgen(n, true, Z); +} + +void +boolgen(Node *n, int true, Node *nn) +{ + int o; + Prog *p1, *p2; + Node *l, *r, nod, nod1; + int32 curs; + + if(debug['g']) { + prtree(nn, "boolgen lhs"); + prtree(n, "boolgen"); + } + curs = cursafe; + l = n->left; + r = n->right; + switch(n->op) { + + default: + if(n->op == OCONST) { + o = vconst(n); + if(!true) + o = !o; + gbranch(OGOTO); + if(o) { + p1 = p; + gbranch(OGOTO); + patch(p1, pc); + } + goto com; + } + regalloc(&nod, n, nn); + cgen(n, &nod); + o = ONE; + if(true) + o = comrel[relindex(o)]; + if(typefd[n->type->etype]) { + nodreg(&nod1, n, NREG+FREGZERO); + gopcode(o, &nod, Z, &nod1); + } else + gopcode(o, &nod, Z, nodconst(0)); + regfree(&nod); + goto com; + + case OCOMMA: + cgen(l, Z); + boolgen(r, true, nn); + break; + + case ONOT: + boolgen(l, !true, nn); + break; + + case OCOND: + bcgen(l, 1); + p1 = p; + bcgen(r->left, true); + p2 = p; + gbranch(OGOTO); + patch(p1, pc); + p1 = p; + bcgen(r->right, !true); + patch(p2, pc); + p2 = p; + gbranch(OGOTO); + patch(p1, pc); + patch(p2, pc); + goto com; + + case OANDAND: + if(!true) + goto caseor; + + caseand: + bcgen(l, true); + p1 = p; + bcgen(r, !true); + p2 = p; + patch(p1, pc); + gbranch(OGOTO); + patch(p2, pc); + goto com; + + case OOROR: + if(!true) + goto caseand; + + caseor: + bcgen(l, !true); + p1 = p; + bcgen(r, !true); + p2 = p; + gbranch(OGOTO); + patch(p1, pc); + patch(p2, pc); + goto com; + + case OEQ: + case ONE: + case OLE: + case OLT: + case OGE: + case OGT: + case OHI: + case OHS: + case OLO: + case OLS: + o = n->op; + if(true) + o = comrel[relindex(o)]; + if(l->complex >= FNX && r->complex >= FNX) { - regret(&nod, r); ++ regret(&nod, r, 0, 0); + cgen(r, &nod); + regsalloc(&nod1, r); + gopcode(OAS, &nod, Z, &nod1); + regfree(&nod); + nod = *n; + nod.right = &nod1; + boolgen(&nod, true, nn); + break; + } + if(sconst(r)) { + regalloc(&nod, l, nn); + cgen(l, &nod); + gopcode(o, &nod, Z, r); + regfree(&nod); + goto com; + } + if(l->complex >= r->complex) { + regalloc(&nod1, l, nn); + cgen(l, &nod1); + regalloc(&nod, r, Z); + cgen(r, &nod); + } else { + regalloc(&nod, r, nn); + cgen(r, &nod); + regalloc(&nod1, l, Z); + cgen(l, &nod1); + } + gopcode(o, &nod1, Z, &nod); + regfree(&nod); + regfree(&nod1); + + com: + if(nn != Z) { + p1 = p; + gopcode(OAS, nodconst(1L), Z, nn); + gbranch(OGOTO); + p2 = p; + patch(p1, pc); + gopcode(OAS, nodconst(0L), Z, nn); + patch(p2, pc); + } + break; + } + cursafe = curs; +} + +void +sugen(Node *n, Node *nn, int32 w) +{ + Prog *p1; + Node nod0, nod1, nod2, nod3, nod4, *l, *r; + Type *t; + int32 pc1; + int i, m, c; + + if(n == Z || n->type == T) + return; + if(debug['g']) { + prtree(nn, "sugen lhs"); + prtree(n, "sugen"); + } + if(nn == nodrat) + if(w > nrathole) + nrathole = w; + switch(n->op) { + case OIND: + if(nn == Z) { + nullwarn(n->left, Z); + break; + } + + default: + goto copy; + + case OCONST: + if(n->type && typev[n->type->etype]) { + if(nn == Z) { + nullwarn(n->left, Z); + break; + } + + t = nn->type; + nn->type = types[TLONG]; + reglcgen(&nod1, nn, Z); + nn->type = t; + + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ + gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1); + else + gopcode(OAS, nod32const(n->vconst), Z, &nod1); + nod1.xoffset += SZ_LONG; + if(align(0, types[TCHAR], Aarg1, nil)) /* isbigendian */ + gopcode(OAS, nod32const(n->vconst), Z, &nod1); + else + gopcode(OAS, nod32const(n->vconst>>32), Z, &nod1); + + regfree(&nod1); + break; + } + goto copy; + + case ODOT: + l = n->left; + sugen(l, nodrat, l->type->width); + if(nn != Z) { + warn(n, "non-interruptable temporary"); + nod1 = *nodrat; + r = n->right; + if(!r || r->op != OCONST) { + diag(n, "DOT and no offset"); + break; + } + nod1.xoffset += (int32)r->vconst; + nod1.type = n->type; + sugen(&nod1, nn, w); + } + break; + + case OSTRUCT: + /* + * rewrite so lhs has no side effects + */ + if(nn != Z && side(nn)) { + nod1 = *n; + nod1.type = typ(TIND, n->type); + regalloc(&nod2, &nod1, Z); + lcgen(nn, &nod2); + regsalloc(&nod0, &nod1); + gopcode(OAS, &nod2, Z, &nod0); + regfree(&nod2); + + nod1 = *n; + nod1.op = OIND; + nod1.left = &nod0; + nod1.right = Z; + nod1.complex = 1; + + sugen(n, &nod1, w); + return; + } + + r = n->left; + for(t = n->type->link; t != T; t = t->down) { + l = r; + if(r->op == OLIST) { + l = r->left; + r = r->right; + } + if(nn == Z) { + cgen(l, nn); + continue; + } + /* + * hand craft *(&nn + o) = l + */ + nod0 = znode; + nod0.op = OAS; + nod0.type = t; + nod0.left = &nod1; + nod0.right = l; + + nod1 = znode; + nod1.op = OIND; + nod1.type = t; + nod1.left = &nod2; + + nod2 = znode; + nod2.op = OADD; + nod2.type = typ(TIND, t); + nod2.left = &nod3; + nod2.right = &nod4; + + nod3 = znode; + nod3.op = OADDR; + nod3.type = nod2.type; + nod3.left = nn; + + nod4 = znode; + nod4.op = OCONST; + nod4.type = nod2.type; + nod4.vconst = t->offset; + + ccom(&nod0); + acom(&nod0); + xcom(&nod0); + nod0.addable = 0; + + /* prtree(&nod0, "hand craft"); /* */ + cgen(&nod0, Z); + } + break; + + case OAS: + if(nn == Z) { + if(n->addable < INDEXED) + sugen(n->right, n->left, w); + break; + } + /* BOTCH -- functions can clobber rathole */ + sugen(n->right, nodrat, w); + warn(n, "non-interruptable temporary"); + sugen(nodrat, n->left, w); + sugen(nodrat, nn, w); + break; + + case OFUNC: ++ if(!hasdotdotdot(n->left->type)) { ++ cgen(n, Z); ++ if(nn != Z) { ++ curarg -= n->type->width; ++ regret(&nod1, n, n->left->type, 1); ++ if(nn->complex >= FNX) { ++ regsalloc(&nod2, n); ++ cgen(&nod1, &nod2); ++ nod1 = nod2; ++ } ++ cgen(&nod1, nn); ++ } ++ break; ++ } + if(nn == Z) { + sugen(n, nodrat, w); + break; + } + if(nn->op != OIND) { + nn = new1(OADDR, nn, Z); + nn->type = types[TIND]; + nn->addable = 0; + } else + nn = nn->left; + n = new(OFUNC, n->left, new(OLIST, nn, n->right)); + n->type = types[TVOID]; + n->left->type = types[TVOID]; + cgen(n, Z); + break; + + case OCOND: + bcgen(n->left, 1); + p1 = p; + sugen(n->right->left, nn, w); + gbranch(OGOTO); + patch(p1, pc); + p1 = p; + sugen(n->right->right, nn, w); + patch(p1, pc); + break; + + case OCOMMA: + cgen(n->left, Z); + sugen(n->right, nn, w); + break; + } + return; + +copy: + if(nn == Z) + return; + if(n->complex >= FNX && nn->complex >= FNX) { + t = nn->type; + nn->type = types[TLONG]; + regialloc(&nod1, nn, Z); + lcgen(nn, &nod1); + regsalloc(&nod2, nn); + nn->type = t; + + gopcode(OAS, &nod1, Z, &nod2); + regfree(&nod1); + + nod2.type = typ(TIND, t); + + nod1 = nod2; + nod1.op = OIND; + nod1.left = &nod2; + nod1.right = Z; + nod1.complex = 1; + nod1.type = t; + + sugen(n, &nod1, w); + return; + } + + if(n->complex > nn->complex) { + t = n->type; + n->type = types[TLONG]; + reglcgen(&nod1, n, Z); + n->type = t; + + t = nn->type; + nn->type = types[TLONG]; + reglcgen(&nod2, nn, Z); + nn->type = t; + } else { + t = nn->type; + nn->type = types[TLONG]; + reglcgen(&nod2, nn, Z); + nn->type = t; + + t = n->type; + n->type = types[TLONG]; + reglcgen(&nod1, n, Z); + n->type = t; + } + + w /= SZ_LONG; + if(w <= 5) { + layout(&nod1, &nod2, w, 0, Z); + goto out; + } + + /* + * minimize space for unrolling loop + * 3,4,5 times. (6 or more is never minimum) + * if small structure, try 2 also. + */ + c = 0; /* set */ + m = 100; + i = 3; + if(w <= 15) + i = 2; + for(; i<=5; i++) + if(i + w%i <= m) { + c = i; + m = c + w%c; + } + + regalloc(&nod3, ®node, Z); + layout(&nod1, &nod2, w%c, w/c, &nod3); + + pc1 = pc; + layout(&nod1, &nod2, c, 0, Z); + + gopcode(OSUB, nodconst(1L), Z, &nod3); + nod1.op = OREGISTER; + gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod1); + nod2.op = OREGISTER; + gopcode(OADD, nodconst(c*SZ_LONG), Z, &nod2); + + gopcode(OGT, &nod3, Z, nodconst(0)); + patch(p, pc1); + + regfree(&nod3); +out: + regfree(&nod1); + regfree(&nod2); +} + +void +layout(Node *f, Node *t, int c, int cv, Node *cn) +{ + Node t1, t2; + + while(c > 3) { + layout(f, t, 2, 0, Z); + c -= 2; + } + + regalloc(&t1, ®node, Z); + regalloc(&t2, ®node, Z); + if(c > 0) { + gopcode(OAS, f, Z, &t1); + f->xoffset += SZ_LONG; + } + if(cn != Z) + gopcode(OAS, nodconst(cv), Z, cn); + if(c > 1) { + gopcode(OAS, f, Z, &t2); + f->xoffset += SZ_LONG; + } + if(c > 0) { + gopcode(OAS, &t1, Z, t); + t->xoffset += SZ_LONG; + } + if(c > 2) { + gopcode(OAS, f, Z, &t1); + f->xoffset += SZ_LONG; + } + if(c > 1) { + gopcode(OAS, &t2, Z, t); + t->xoffset += SZ_LONG; + } + if(c > 2) { + gopcode(OAS, &t1, Z, t); + t->xoffset += SZ_LONG; + } + regfree(&t1); + regfree(&t2); +} diff --cc src/cmd/9c/gc.h index 82204902b7,0000000000..fbe5099fe8 mode 100644,000000..100644 --- a/src/cmd/9c/gc.h +++ b/src/cmd/9c/gc.h @@@ -1,351 -1,0 +1,350 @@@ +// cmd/9c/gc.h from Vita Nuova. +// +// 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-2008 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-2008 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 +#include "../cc/cc.h" +#include "../9l/9.out.h" + +/* + * 9c/powerpc64 + */ +#define SZ_CHAR 1 +#define SZ_SHORT 2 +#define SZ_INT 4 +#define SZ_LONG 4 +#define SZ_IND 8 +#define SZ_FLOAT 4 +#define SZ_VLONG 8 +#define SZ_DOUBLE 8 +#define FNX 100 + +typedef struct Case Case; +typedef struct C1 C1; +typedef struct Multab Multab; +typedef struct Hintab Hintab; +typedef struct Reg Reg; +typedef struct Rgn Rgn; + +#define A ((Adr*)0) + +#define INDEXED 9 +#define P ((Prog*)0) + +struct Case +{ + Case* link; + vlong val; + int32 label; + char def; + char isv; +}; +#define C ((Case*)0) + +struct C1 +{ + vlong val; + int32 label; +}; + +struct Multab +{ + int32 val; + char code[20]; +}; + +struct Hintab +{ + ushort val; + char hint[10]; +}; + +struct Reg +{ + int32 pc; + int32 rpo; /* reverse post ordering */ + + Bits set; + Bits use1; + Bits use2; + + Bits refbehind; + Bits refahead; + Bits calbehind; + Bits calahead; + Bits regdiff; + Bits act; + + int32 regu; + int32 loop; /* could be shorter */ + + union + { + Reg* log5; + int32 active; + }; + Reg* p1; + Reg* p2; + Reg* p2link; + Reg* s1; + Reg* s2; + Reg* link; + Prog* prog; +}; +#define R ((Reg*)0) + +#define NRGN 600 +struct Rgn +{ + Reg* enter; + short cost; + short varno; + short regno; +}; + +EXTERN int32 breakpc; +EXTERN int32 nbreak; +EXTERN Case* cases; +EXTERN Node constnode; +EXTERN Node fconstnode; +EXTERN Node vconstnode; +EXTERN int32 continpc; +EXTERN int32 curarg; +EXTERN int32 cursafe; +EXTERN Prog* lastp; +extern int hintabsize; +EXTERN int32 maxargsafe; +EXTERN Multab multab[20]; +EXTERN int mnstring; - EXTERN int retok; +EXTERN Node* nodrat; +EXTERN Node* nodret; +EXTERN Node* nodsafe; +EXTERN int32 nrathole; +EXTERN int32 nstring; +EXTERN Prog* p; +EXTERN int32 pc; +EXTERN Node regnode; +EXTERN Node qregnode; +EXTERN char string[NSNAME]; +EXTERN Sym* symrathole; +EXTERN Node znode; +EXTERN Prog zprog; +EXTERN int reg[NREG+NREG]; +EXTERN int32 exregoffset; +EXTERN int32 exfregoffset; +EXTERN uchar typechlpv[NTYPE]; + +#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 bset(a,n) ((a).b[(n)/32]&(1L<<(n)%32)) + +#define CLOAD 5 +#define CREF 5 +#define CINF 1000 +#define LOOP 3 + +EXTERN Rgn region[NRGN]; +EXTERN Rgn* rgp; +EXTERN int nregion; +EXTERN int nvar; + +EXTERN Bits externs; +EXTERN Bits params; +EXTERN Bits consts; +EXTERN Bits addrs; + +EXTERN int32 regbits; +EXTERN int32 exregbits; + +EXTERN int change; +EXTERN int suppress; + +EXTERN Reg* firstr; +EXTERN Reg* lastr; +EXTERN Reg zreg; +EXTERN Reg* freer; +EXTERN Var var[NVAR]; +EXTERN int32* idom; +EXTERN Reg** rpo2r; +EXTERN int32 maxnr; + +#define R0ISZERO (debug['0']==0) + +extern char* anames[]; +extern Hintab hintab[]; + +/* + * sgen.c + */ +void codgen(Node*, Node*); +void gen(Node*); +void usedset(Node*, int); +void noretval(int); +void xcom(Node*); +int bcomplex(Node*, Node*); +Prog* gtext(Sym*, int32); - vlong argsize(void); ++vlong argsize(int); + +/* + * cgen.c + */ +void cgen(Node*, Node*); +void reglcgen(Node*, Node*, Node*); +void lcgen(Node*, Node*); +void bcgen(Node*, int); +void boolgen(Node*, int, Node*); +void sugen(Node*, Node*, int32); +void layout(Node*, Node*, int, int, Node*); + +/* + * txt.c + */ +void ginit(void); +void gclean(void); +void nextpc(void); +void gargs(Node*, Node*, Node*); +void garg1(Node*, Node*, Node*, int, Node**); +Node* nodconst(int32); +Node* nod32const(vlong); +Node* nodfconst(double); +Node* nodgconst(vlong v, Type *t); +void nodreg(Node*, Node*, int); - void regret(Node*, Node*); ++void regret(Node*, Node*, Type*, int); +void regalloc(Node*, Node*, Node*); +void regfree(Node*); +void regialloc(Node*, Node*, Node*); +void regsalloc(Node*, Node*); +void regaalloc1(Node*, Node*); +void regaalloc(Node*, Node*); +void regind(Node*, Node*); +void gprep(Node*, Node*); +void raddr(Node*, Prog*); +void naddr(Node*, Addr*); +void gmove(Node*, Node*); +void gins(int a, Node*, Node*); +void gopcode(int, Node*, Node*, Node*); +int samaddr(Node*, Node*); +void gbranch(int); +int immconst(Node*); +void patch(Prog*, int32); +int sconst(Node*); +int sval(int32); +int uconst(Node*); +void gpseudo(int, Sym*, Node*); +void gprefetch(Node*); +void gpcdata(int, int); + +/* + * swt.c + */ +int swcmp(const void*, const void*); +void doswit(Node*); +void swit1(C1*, int, int32, Node*); +void swit2(C1*, int, int32, Node*, Node*); +void newcase(void); +void bitload(Node*, Node*, Node*, Node*, Node*); +void bitstore(Node*, Node*, Node*, Node*, Node*); +int32 outstring(char*, int32); +int mulcon(Node*, Node*); +Multab* mulcon0(Node*, int32); +int mulcon1(Node*, int32, Node*); +void nullwarn(Node*, Node*); +void sextern(Sym*, Node*, int32, int32); +void gextern(Sym*, Node*, int32, int32); +void outcode(void); + +/* + * list + */ +void listinit(void); +int Pconv(Fmt*); +int Aconv(Fmt*); +int Dconv(Fmt*); +int Sconv(Fmt*); +int Nconv(Fmt*); +int Bconv(Fmt*); + +/* + * reg.c + */ +Reg* rega(void); +int rcmp(const void*, const void*); +void regopt(Prog*); +void addmove(Reg*, int, int, int); +Bits mkvar(Addr*, int); +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); +void paint3(Reg*, int, int32, int); +void addreg(Addr*, int); + +/* + * peep.c + */ +void peep(void); +void excise(Reg*); +Reg* uniqp(Reg*); +Reg* uniqs(Reg*); +int regtyp(Addr*); +int regzer(Addr*); +int anyvar(Addr*); +int subprop(Reg*); +int copyprop(Reg*); +int copy1(Addr*, Addr*, Reg*, int); +int copyu(Prog*, Addr*, Addr*); + +int copyas(Addr*, Addr*); +int copyau(Addr*, Addr*); +int copyau1(Prog*, Addr*); +int copysub(Addr*, Addr*, Addr*, int); +int copysub1(Prog*, Addr*, Addr*, int); + +int32 RtoB(int); +int32 FtoB(int); +int BtoR(int32); +int BtoF(int32); + +/* + * com64.c + */ +int com64(Node*); +void com64init(void); +void bool64(Node*); + +#pragma varargck type "A" int +#pragma varargck type "B" Bits +#pragma varargck type "D" Addr* +#pragma varargck type "N" Addr* +#pragma varargck type "P" Prog* +#pragma varargck type "S" char* diff --cc src/cmd/9c/sgen.c index 25f0438e58,0000000000..b03c17267d mode 100644,000000..100644 --- a/src/cmd/9c/sgen.c +++ b/src/cmd/9c/sgen.c @@@ -1,291 -1,0 +1,291 @@@ +// cmd/9c/sgen.c from Vita Nuova. +// +// 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-2008 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-2008 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 "gc.h" + +Prog* +gtext(Sym *s, int32 stkoff) +{ + vlong v; + - v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff); ++ v = ((uvlong)argsize(1) << 32) | (stkoff & 0xffffffff); + if((textflag & NOSPLIT) && stkoff >= 128) + yyerror("stack frame too large for NOSPLIT function"); + + gpseudo(ATEXT, s, nodgconst(v, types[TVLONG])); + return p; +} + + +void +noretval(int n) +{ + + if(n & 1) { + gins(ANOP, Z, Z); + p->to.type = D_REG; + p->to.reg = REGRET; + } + if(n & 2) { + gins(ANOP, Z, Z); + p->to.type = D_FREG; + p->to.reg = FREGRET; + } +} + +/* + * calculate addressability as follows + * CONST ==> 20 $value + * NAME ==> 10 name + * REGISTER ==> 11 register + * INDREG ==> 12 *[(reg)+offset] + * &10 ==> 2 $name + * ADD(2, 20) ==> 2 $name+offset + * ADD(3, 20) ==> 3 $(reg)+offset + * &12 ==> 3 $(reg)+offset + * *11 ==> 11 ?? + * *2 ==> 10 name + * *3 ==> 12 *(reg)+offset + * calculate complexity (number of registers) + */ +void +xcom(Node *n) +{ + Node *l, *r; + int v; + + if(n == Z) + return; + l = n->left; + r = n->right; + n->addable = 0; + n->complex = 0; + switch(n->op) { + case OCONST: + n->addable = 20; + return; + + case OREGISTER: + n->addable = 11; + return; + + case OINDREG: + n->addable = 12; + return; + + case ONAME: + n->addable = 10; + return; + + case OADDR: + xcom(l); + if(l->addable == 10) + n->addable = 2; + if(l->addable == 12) + n->addable = 3; + break; + + case OIND: + xcom(l); + if(l->addable == 11) + n->addable = 12; + if(l->addable == 3) + n->addable = 12; + if(l->addable == 2) + n->addable = 10; + break; + + case OADD: + xcom(l); + xcom(r); + if(l->addable == 20) { + if(r->addable == 2) + n->addable = 2; + if(r->addable == 3) + n->addable = 3; + } + if(r->addable == 20) { + if(l->addable == 2) + n->addable = 2; + if(l->addable == 3) + n->addable = 3; + } + break; + + case OASMUL: + case OASLMUL: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OASASHL; + r->vconst = v; + r->type = types[TINT]; + } + break; + + case OMUL: + case OLMUL: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OASHL; + r->vconst = v; + r->type = types[TINT]; + } + v = vlog(l); + if(v >= 0) { + n->op = OASHL; + n->left = r; + n->right = l; + r = l; + l = n->left; + r->vconst = v; + r->type = types[TINT]; + simplifyshift(n); + } + break; + + case OASLDIV: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OASLSHR; + r->vconst = v; + r->type = types[TINT]; + } + break; + + case OLDIV: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OLSHR; + r->vconst = v; + r->type = types[TINT]; + simplifyshift(n); + } + break; + + case OASLMOD: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OASAND; + r->vconst--; + } + break; + + case OLMOD: + xcom(l); + xcom(r); + v = vlog(r); + if(v >= 0) { + n->op = OAND; + r->vconst--; + } + break; + + case OLSHR: + case OASHL: + case OASHR: + xcom(l); + xcom(r); + simplifyshift(n); + break; + + default: + if(l != Z) + xcom(l); + if(r != Z) + xcom(r); + break; + } + if(n->addable >= 10) + return; + if(l != Z) + n->complex = l->complex; + if(r != Z) { + if(r->complex == n->complex) + n->complex = r->complex+1; + else + if(r->complex > n->complex) + n->complex = r->complex; + } + if(n->complex == 0) + n->complex++; + +// if(com64(n)) +// return; + + switch(n->op) { + + case OFUNC: + n->complex = FNX; + break; + + case OEQ: + case ONE: + case OLE: + case OLT: + case OGE: + case OGT: + case OHI: + case OHS: + case OLO: + case OLS: + /* + * immediate operators, make const on right + */ + if(l->op == OCONST) { + n->left = r; + n->right = l; + n->op = invrel[relindex(n->op)]; + } + break; + + case OADD: + case OXOR: + case OAND: + case OOR: + /* + * immediate operators, make const on right + */ + if(l->op == OCONST) { + n->left = r; + n->right = l; + } + break; + } +} + diff --cc src/cmd/9c/swt.c index 83058e7ab9,0000000000..a63db60b27 mode 100644,000000..100644 --- a/src/cmd/9c/swt.c +++ b/src/cmd/9c/swt.c @@@ -1,404 -1,0 +1,407 @@@ +// cmd/9c/swt.c from Vita Nuova. +// +// 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-2008 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-2008 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 "gc.h" + +void +swit1(C1 *q, int nc, int32 def, Node *n) +{ + Node tn, nod; + + regalloc(&nod, n, Z); + /* always signed */ + if(typev[n->type->etype]) + nod.type = types[TVLONG]; + else + nod.type = types[TLONG]; + cgen(n, &nod); + regalloc(&tn, ®node, Z); + swit2(q, nc, def, &nod, &tn); + regfree(&tn); + regfree(&nod); +} + +void +swit2(C1 *q, int nc, int32 def, Node *n, Node *tn) +{ + C1 *r; + int i; + Prog *sp; + + if(nc < 5) { + for(i=0; ival)) { + gopcode(OEQ, n, Z, nodconst(q->val)); + } else { + gopcode(OSUB, nodconst(q->val), n, tn); + gopcode(OEQ, tn, Z, nodconst(0)); + } + patch(p, q->label); + q++; + } + gbranch(OGOTO); + patch(p, def); + return; + } + i = nc / 2; + r = q+i; + if(sval(r->val)) { + gopcode(OGT, n, Z, nodconst(r->val)); + sp = p; + } else { + gopcode(OSUB, nodconst(r->val), n, tn); + gopcode(OGT, tn, Z, nodconst(0)); + sp = p; + } + gbranch(OGOTO); + p->as = ABEQ; + patch(p, r->label); + swit2(q, i, def, n, tn); + + patch(sp, pc); + swit2(r+1, nc-i-1, def, n, tn); +} + +void +bitload(Node *b, Node *n1, Node *n2, Node *n3, Node *nn) +{ + int sh; + int32 v; + Node *l; + + /* + * n1 gets adjusted/masked value + * n2 gets address of cell + * n3 gets contents of cell + */ + l = b->left; + if(n2 != Z) { + regalloc(n1, l, nn); + reglcgen(n2, l, Z); + regalloc(n3, l, Z); + gopcode(OAS, n2, Z, n3); + gopcode(OAS, n3, Z, n1); + } else { + regalloc(n1, l, nn); + cgen(l, n1); + } + if(b->type->shift == 0 && typeu[b->type->etype]) { + v = ~0 + (1L << b->type->nbits); + gopcode(OAND, nodconst(v), Z, n1); + } else { + sh = 32 - b->type->shift - b->type->nbits; + if(sh > 0) + gopcode(OASHL, nodconst(sh), Z, n1); + sh += b->type->shift; + if(sh > 0) + if(typeu[b->type->etype]) + gopcode(OLSHR, nodconst(sh), Z, n1); + else + gopcode(OASHR, nodconst(sh), Z, n1); + } +} + +void +bitstore(Node *b, Node *n1, Node *n2, Node *n3, Node *nn) +{ + int32 v; + Node nod, *l; + int sh; + + /* + * n1 has adjusted/masked value + * n2 has address of cell + * n3 has contents of cell + */ + l = b->left; + regalloc(&nod, l, Z); + v = ~0 + (1L << b->type->nbits); + gopcode(OAND, nodconst(v), Z, n1); + gopcode(OAS, n1, Z, &nod); + if(nn != Z) + gopcode(OAS, n1, Z, nn); + sh = b->type->shift; + if(sh > 0) + gopcode(OASHL, nodconst(sh), Z, &nod); + v <<= sh; + gopcode(OAND, nodconst(~v), Z, n3); + gopcode(OOR, n3, Z, &nod); + gopcode(OAS, &nod, Z, n2); + + regfree(&nod); + regfree(n1); + regfree(n2); + regfree(n3); +} + +int32 +outstring(char *s, int32 n) +{ + int32 r; + + if(suppress) + return nstring; + r = nstring; + while(n) { + string[mnstring] = *s++; + mnstring++; + nstring++; + if(mnstring >= NSNAME) { + gpseudo(ADATA, symstring, nodconst(0L)); + p->from.offset += nstring - NSNAME; + p->reg = NSNAME; + p->to.type = D_SCONST; + memmove(p->to.u.sval, string, NSNAME); + mnstring = 0; + } + n--; + } + return r; +} + +int +mulcon(Node *n, Node *nn) +{ + Node *l, *r, nod1, nod2; + Multab *m; + int32 v; + int o; + char code[sizeof(m->code)+2], *p; + + if(typefd[n->type->etype]) + return 0; + l = n->left; + r = n->right; + if(l->op == OCONST) { + l = r; + r = n->left; + } + if(r->op != OCONST) + return 0; + v = convvtox(r->vconst, n->type->etype); + if(v != r->vconst) { + if(debug['M']) + print("%L multiply conv: %lld\n", n->lineno, r->vconst); + return 0; + } + m = mulcon0(n, v); + if(!m) { + if(debug['M']) + print("%L multiply table: %lld\n", n->lineno, r->vconst); + return 0; + } + + memmove(code, m->code, sizeof(m->code)); + code[sizeof(m->code)] = 0; + + p = code; + if(p[1] == 'i') + p += 2; + regalloc(&nod1, n, nn); + cgen(l, &nod1); + if(v < 0) + gopcode(ONEG, &nod1, Z, &nod1); + regalloc(&nod2, n, Z); + +loop: + switch(*p) { + case 0: + regfree(&nod2); + gopcode(OAS, &nod1, Z, nn); + regfree(&nod1); + return 1; + case '+': + o = OADD; + goto addsub; + case '-': + o = OSUB; + addsub: /* number is r,n,l */ + v = p[1] - '0'; + r = &nod1; + if(v&4) + r = &nod2; + n = &nod1; + if(v&2) + n = &nod2; + l = &nod1; + if(v&1) + l = &nod2; + gopcode(o, l, n, r); + break; + default: /* op is shiftcount, number is r,l */ + v = p[1] - '0'; + r = &nod1; + if(v&2) + r = &nod2; + l = &nod1; + if(v&1) + l = &nod2; + v = *p - 'a'; + if(v < 0 || v >= 32) { + diag(n, "mulcon unknown op: %c%c", p[0], p[1]); + break; + } + gopcode(OASHL, nodconst(v), l, r); + break; + } + p += 2; + goto loop; +} + +void +sextern(Sym *s, Node *a, int32 o, int32 w) +{ + int32 e, lw; + + for(e=0; efrom.offset += o+e; + p->reg = lw; + p->to.type = D_SCONST; + memmove(p->to.u.sval, a->cstring+e, lw); + } +} + +void +gextern(Sym *s, Node *a, int32 o, int32 w) +{ + gpseudo(ADATA, s, a); + p->from.offset += o; + p->reg = w; + if(p->to.type == D_OREG) + p->to.type = D_CONST; +} + +void +outcode(void) +{ + Bprint(&outbuf, "go object %s %s %s\n", getgoos(), getgoarch(), getgoversion()); + if(pragcgobuf.to > pragcgobuf.start) { + Bprint(&outbuf, "\n"); + Bprint(&outbuf, "$$ // exports\n\n"); + Bprint(&outbuf, "$$ // local types\n\n"); + Bprint(&outbuf, "$$ // cgo\n"); + Bprint(&outbuf, "%s", fmtstrflush(&pragcgobuf)); + Bprint(&outbuf, "\n$$\n\n"); + } + Bprint(&outbuf, "!\n"); + + writeobj(ctxt, &outbuf); + lastp = nil; +} + +int32 +align(int32 i, Type *t, int op, int32 *maxalign) +{ + int32 o; + Type *v; - int w; ++ int w, packw; + + o = i; + w = 1; ++ packw = 0; + switch(op) { + default: + diag(Z, "unknown align opcode %d", op); + break; + + case Asu2: /* padding at end of a struct */ + w = *maxalign; + if(w < 1) + w = 1; + if(packflg) - w = packflg; ++ packw = packflg; + break; + + case Ael1: /* initial allign of struct element */ + for(v=t; v->etype==TARRAY; v=v->link) + ; + if(v->etype == TSTRUCT || v->etype == TUNION) + w = v->align; + else + w = ewidth[v->etype]; + if(w < 1 || w > SZ_VLONG) + fatal(Z, "align"); + if(packflg) - w = packflg; ++ packw = packflg; + break; + + case Ael2: /* width of a struct element */ + o += t->width; + break; + + case Aarg0: /* initial passbyptr argument in arg list */ + if(typesu[t->etype]) { + o = align(o, types[TIND], Aarg1, nil); + o = align(o, types[TIND], Aarg2, nil); + } + break; + + case Aarg1: /* initial align of parameter */ + w = ewidth[t->etype]; + if(w <= 0 || w >= SZ_VLONG) { + w = SZ_VLONG; + break; + } + w = 1; + break; + + case Aarg2: /* width of a parameter */ + o += t->width; + w = t->width; + if(w > SZ_VLONG) + w = SZ_VLONG; + break; + + case Aaut3: /* total align of automatic */ + o = align(o, t, Ael1, nil); + o = align(o, t, Ael2, nil); + break; + } ++ if(packw != 0 && xround(o, w) != xround(o, packw)) ++ diag(Z, "#pragma pack changes offset of %T", t); + o = xround(o, w); + if(maxalign && *maxalign < w) + *maxalign = w; + if(debug['A']) + print("align %s %ld %T = %ld\n", bnames[op], i, t, o); + return o; +} + +int32 +maxround(int32 max, int32 v) +{ + v = xround(v, SZ_VLONG); + if(v > max) + return v; + return max; +} diff --cc src/cmd/9c/txt.c index 84aeb4d8a9,0000000000..e46aba84e7 mode 100644,000000..100644 --- a/src/cmd/9c/txt.c +++ b/src/cmd/9c/txt.c @@@ -1,1509 -1,0 +1,1537 @@@ +// cmd/9c/txt.c from Vita Nuova. +// +// 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-2008 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-2008 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 "gc.h" + +static int resvreg[nelem(reg)]; + +#define isv(et) ((et) == TVLONG || (et) == TUVLONG || (et) == TIND) + +int thechar = '9'; +char *thestring = "power64"; + +LinkArch *thelinkarch; + +void +linkarchinit(void) +{ + thestring = getgoarch(); + if(strcmp(thestring, "power64le") == 0) + thelinkarch = &linkpower64le; + else + thelinkarch = &linkpower64; +} + + +void +ginit(void) +{ + Type *t; + + dodefine("_64BITREG"); + dodefine("_64BIT"); + exregoffset = REGEXT; + exfregoffset = FREGEXT; + listinit(); + nstring = 0; + mnstring = 0; + nrathole = 0; + pc = 0; + breakpc = -1; + continpc = -1; + cases = C; + lastp = P; + tfield = types[TLONG]; + + typeword = typechlvp; + typecmplx = typesu; + /* TO DO */ + memmove(typechlpv, typechlp, sizeof(typechlpv)); + typechlpv[TVLONG] = 1; + typechlpv[TUVLONG] = 1; + + zprog.link = P; + zprog.as = AGOK; + zprog.reg = NREG; + zprog.from.type = D_NONE; + zprog.from.name = D_NONE; + zprog.from.reg = NREG; + zprog.from3 = zprog.from; + zprog.to = zprog.from; + + regnode.op = OREGISTER; + regnode.class = CEXREG; + regnode.reg = 0; + regnode.complex = 0; + regnode.addable = 11; + regnode.type = types[TLONG]; + + qregnode = regnode; + qregnode.type = types[TVLONG]; + + constnode.op = OCONST; + constnode.class = CXXX; + constnode.complex = 0; + constnode.addable = 20; + constnode.type = types[TLONG]; + + vconstnode = constnode; + vconstnode.type = types[TVLONG]; + + fconstnode.op = OCONST; + fconstnode.class = CXXX; + fconstnode.complex = 0; + fconstnode.addable = 20; + fconstnode.type = types[TDOUBLE]; + + nodsafe = new(ONAME, Z, Z); + nodsafe->sym = slookup(".safe"); + nodsafe->type = types[TINT]; + nodsafe->etype = types[TINT]->etype; + nodsafe->class = CAUTO; + complex(nodsafe); + + t = typ(TARRAY, types[TCHAR]); + symrathole = slookup(".rathole"); + symrathole->class = CGLOBL; + symrathole->type = t; + + nodrat = new(ONAME, Z, Z); + nodrat->sym = symrathole; + nodrat->type = types[TIND]; + nodrat->etype = TVOID; + nodrat->class = CGLOBL; + complex(nodrat); + nodrat->type = t; + + nodret = new(ONAME, Z, Z); + nodret->sym = slookup(".ret"); + nodret->type = types[TIND]; + nodret->etype = TIND; + nodret->class = CPARAM; + nodret = new(OIND, nodret, Z); + complex(nodret); + + com64init(); + + memset(reg, 0, sizeof(reg)); + reg[REGZERO] = 1; /* don't use */ + reg[REGTMP] = 1; + reg[FREGCVI+NREG] = 1; + reg[FREGZERO+NREG] = 1; + reg[FREGHALF+NREG] = 1; + reg[FREGONE+NREG] = 1; + reg[FREGTWO+NREG] = 1; + memmove(resvreg, reg, sizeof(reg)); +} + +void +gclean(void) +{ + int i; + Sym *s; + + for(i=0; itype->width = nstring; + symrathole->type->width = nrathole; + for(i=0; ilink) { + if(s->type == T) + continue; + if(s->type->width == 0) + continue; + if(s->class != CGLOBL && s->class != CSTATIC) + continue; + if(s->type == types[TENUM]) + continue; + gpseudo(AGLOBL, s, nodconst(s->type->width)); + } + nextpc(); + p->as = AEND; + outcode(); +} + +void +nextpc(void) +{ + Plist *pl; + + p = alloc(sizeof(*p)); + *p = zprog; + p->lineno = nearln; + p->pc = pc; + pc++; + if(lastp == P) { + pl = linknewplist(ctxt); + pl->firstpc = p; + } else + lastp->link = p; + lastp = p; +} + +void +gargs(Node *n, Node *tn1, Node *tn2) +{ + int32 regs; + Node fnxargs[20], *fnxp; + + regs = cursafe; + + fnxp = fnxargs; + garg1(n, tn1, tn2, 0, &fnxp); /* compile fns to temps */ + + curarg = 0; + fnxp = fnxargs; + garg1(n, tn1, tn2, 1, &fnxp); /* compile normal args and temps */ + + cursafe = regs; +} + +void +garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp) +{ + Node nod; + + if(n == Z) + return; + if(n->op == OLIST) { + garg1(n->left, tn1, tn2, f, fnxp); + garg1(n->right, tn1, tn2, f, fnxp); + return; + } + if(f == 0) { + if(n->complex >= FNX) { + regsalloc(*fnxp, n); + nod = znode; + nod.op = OAS; + nod.left = *fnxp; + nod.right = n; + nod.type = n->type; + cgen(&nod, Z); + (*fnxp)++; + } + return; + } + if(typesu[n->type->etype]) { + regaalloc(tn2, n); + if(n->complex >= FNX) { + sugen(*fnxp, tn2, n->type->width); + (*fnxp)++; + } else + sugen(n, tn2, n->type->width); + return; + } + if(REGARG>=0 && curarg == 0 && typechlpv[n->type->etype]) { + regaalloc1(tn1, n); + if(n->complex >= FNX) { + cgen(*fnxp, tn1); + (*fnxp)++; + } else + cgen(n, tn1); + return; + } + if(vconst(n) == 0) { + regaalloc(tn2, n); + gopcode(OAS, n, Z, tn2); + return; + } + regalloc(tn1, n, Z); + if(n->complex >= FNX) { + cgen(*fnxp, tn1); + (*fnxp)++; + } else + cgen(n, tn1); + regaalloc(tn2, n); + gopcode(OAS, tn1, Z, tn2); + regfree(tn1); +} + +Node* +nod32const(vlong v) +{ + constnode.vconst = v & MASK(32); + return &constnode; +} + +Node* +nodgconst(vlong v, Type *t) +{ + if(!typev[t->etype]) + return nodconst((int32)v); + vconstnode.vconst = v; + return &vconstnode; +} + +Node* +nodconst(int32 v) +{ + constnode.vconst = v; + return &constnode; +} + +Node* +nodfconst(double d) +{ + fconstnode.fconst = d; + return &fconstnode; +} + +void +nodreg(Node *n, Node *nn, int reg) +{ + *n = qregnode; + n->reg = reg; + n->type = nn->type; + n->lineno = nn->lineno; +} + +void - regret(Node *n, Node *nn) ++regret(Node *n, Node *nn, Type *t, int mode) +{ + int r; ++ ++ if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { ++ r = REGRET; ++ if(typefd[nn->type->etype]) ++ r = FREGRET+NREG; ++ nodreg(n, nn, r); ++ reg[r]++; ++ return; ++ } ++ ++ if(mode == 1) { ++ // fetch returned value after call. ++ // already called gargs, so curarg is set. ++ curarg = (curarg+7) & ~7; ++ regaalloc(n, nn); ++ return; ++ } + - r = REGRET; - if(typefd[nn->type->etype]) - r = FREGRET+NREG; - nodreg(n, nn, r); - reg[r]++; ++ if(mode == 2) { ++ // store value to be returned. ++ // must compute arg offset. ++ if(t->etype != TFUNC) ++ fatal(Z, "bad regret func %T", t); ++ *n = *nn; ++ n->op = ONAME; ++ n->class = CPARAM; ++ n->sym = slookup(".ret"); ++ n->complex = nodret->complex; ++ n->addable = 20; ++ n->xoffset = argsize(0); ++ return; ++ } ++ ++ fatal(Z, "bad regret"); +} + +void +regalloc(Node *n, Node *tn, Node *o) +{ + int i, j; + static int lasti; + + switch(tn->type->etype) { + case TCHAR: + case TUCHAR: + case TSHORT: + case TUSHORT: + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + if(o != Z && o->op == OREGISTER) { + i = o->reg; + if(i > 0 && i < NREG) + goto out; + } + j = lasti + REGRET+1; + for(i=REGRET+1; i= NREG) + j = REGRET+1; + if(reg[j] == 0) { + i = j; + goto out; + } + j++; + } + diag(tn, "out of fixed registers"); + goto err; + + case TFLOAT: + case TDOUBLE: + if(o != Z && o->op == OREGISTER) { + i = o->reg; + if(i >= NREG && i < NREG+NREG) + goto out; + } + j = lasti + NREG; + for(i=NREG; i= NREG+NREG) + j = NREG; + if(reg[j] == 0) { + i = j; + goto out; + } + j++; + } + diag(tn, "out of float registers"); + goto err; + } + diag(tn, "unknown type in regalloc: %T", tn->type); +err: + i = 0; +out: + if(i) + reg[i]++; + lasti++; + if(lasti >= 5) + lasti = 0; + nodreg(n, tn, i); +} + +void +regialloc(Node *n, Node *tn, Node *o) +{ + Node nod; + + nod = *tn; + nod.type = types[TIND]; + regalloc(n, &nod, o); +} + +void +regfree(Node *n) +{ + int i; + + i = 0; + if(n->op != OREGISTER && n->op != OINDREG) + goto err; + i = n->reg; + if(i < 0 || i >= sizeof(reg)) + goto err; + if(reg[i] <= 0) + goto err; + reg[i]--; + return; +err: + diag(n, "error in regfree: %d", i); +} + +void +regsalloc(Node *n, Node *nn) +{ + cursafe = align(cursafe, nn->type, Aaut3, nil); + maxargsafe = maxround(maxargsafe, cursafe+curarg); + *n = *nodsafe; + n->xoffset = -(stkoff + cursafe); + n->type = nn->type; + n->etype = nn->type->etype; + n->lineno = nn->lineno; +} + +void +regaalloc1(Node *n, Node *nn) +{ + if(REGARG < 0) + return; + nodreg(n, nn, REGARG); + reg[REGARG]++; + curarg = align(curarg, nn->type, Aarg1, nil); + curarg = align(curarg, nn->type, Aarg2, nil); + maxargsafe = maxround(maxargsafe, cursafe+curarg); +} + +void +regaalloc(Node *n, Node *nn) +{ + curarg = align(curarg, nn->type, Aarg1, nil); + *n = *nn; + n->op = OINDREG; + n->reg = REGSP; + n->xoffset = curarg + SZ_VLONG; + n->complex = 0; + n->addable = 20; + curarg = align(curarg, nn->type, Aarg2, nil); + maxargsafe = maxround(maxargsafe, cursafe+curarg); +} + +void +regind(Node *n, Node *nn) +{ + + if(n->op != OREGISTER) { + diag(n, "regind not OREGISTER"); + return; + } + n->op = OINDREG; + n->type = nn->type; +} + +void +raddr(Node *n, Prog *p) +{ + Addr a; + + naddr(n, &a); + if(R0ISZERO && a.type == D_CONST && a.offset == 0) { + a.type = D_REG; + a.reg = REGZERO; + } + if(a.type != D_REG && a.type != D_FREG) { + if(n) + diag(n, "bad in raddr: %O", n->op); + else + diag(n, "bad in raddr: "); + p->reg = NREG; + } else + p->reg = a.reg; +} + +void +naddr(Node *n, Addr *a) +{ + int32 v; + + a->type = D_NONE; + if(n == Z) + return; + switch(n->op) { + default: + bad: + prtree(n, "naddr"); + diag(n, "%L: !bad in naddr: %O", n->lineno, n->op); + break; + + case OREGISTER: + a->type = D_REG; + a->sym = nil; + a->reg = n->reg; + if(a->reg >= NREG) { + a->type = D_FREG; + a->reg -= NREG; + } + break; + + case OIND: + naddr(n->left, a); + if(a->type == D_REG) { + a->type = D_OREG; + break; + } + if(a->type == D_CONST) { + a->type = D_OREG; + break; + } + goto bad; + + case OINDREG: + a->type = D_OREG; + a->sym = nil; + a->offset = n->xoffset; + a->reg = n->reg; + break; + + case ONAME: + a->etype = n->etype; + a->type = D_OREG; + a->name = D_STATIC; + a->sym = linksym(n->sym); + a->offset = n->xoffset; + if(n->class == CSTATIC) + break; + if(n->class == CEXTERN || n->class == CGLOBL) { + a->name = D_EXTERN; + break; + } + if(n->class == CAUTO) { + a->name = D_AUTO; + break; + } + if(n->class == CPARAM) { + a->name = D_PARAM; + break; + } + goto bad; + + case OCONST: + a->sym = nil; + a->reg = NREG; + if(typefd[n->type->etype]) { + a->type = D_FCONST; + a->u.dval = n->fconst; + } else { + a->type = D_CONST; + a->offset = n->vconst; + } + break; + + case OADDR: + naddr(n->left, a); + if(a->type == D_OREG) { + a->type = D_CONST; + break; + } + goto bad; + + case OADD: + if(n->left->op == OCONST) { + naddr(n->left, a); + v = a->offset; + naddr(n->right, a); + } else { + naddr(n->right, a); + v = a->offset; + naddr(n->left, a); + } + a->offset += v; + break; + + } +} + +void +fop(int as, int f1, int f2, Node *t) +{ + Node nod1, nod2, nod3; + + nodreg(&nod1, t, NREG+f1); + nodreg(&nod2, t, NREG+f2); + regalloc(&nod3, t, t); + gopcode(as, &nod1, &nod2, &nod3); + gmove(&nod3, t); + regfree(&nod3); +} + +void +gmove(Node *f, Node *t) +{ + int ft, tt, a; + Node nod, fxc0, fxc1, fxc2, fxrat; + Prog *p1; + double d; + + ft = f->type->etype; + tt = t->type->etype; + + if(ft == TDOUBLE && f->op == OCONST) { + d = f->fconst; + if(d == 0.0) { + a = FREGZERO; + goto ffreg; + } + if(d == 0.5) { + a = FREGHALF; + goto ffreg; + } + if(d == 1.0) { + a = FREGONE; + goto ffreg; + } + if(d == 2.0) { + a = FREGTWO; + goto ffreg; + } + if(d == -.5) { + fop(OSUB, FREGHALF, FREGZERO, t); + return; + } + if(d == -1.0) { + fop(OSUB, FREGONE, FREGZERO, t); + return; + } + if(d == -2.0) { + fop(OSUB, FREGTWO, FREGZERO, t); + return; + } + if(d == 1.5) { + fop(OADD, FREGONE, FREGHALF, t); + return; + } + if(d == 2.5) { + fop(OADD, FREGTWO, FREGHALF, t); + return; + } + if(d == 3.0) { + fop(OADD, FREGTWO, FREGONE, t); + return; + } + } + if(ft == TFLOAT && f->op == OCONST) { + d = f->fconst; + if(d == 0) { + a = FREGZERO; + ffreg: + nodreg(&nod, f, NREG+a); + gmove(&nod, t); + return; + } + } + /* + * a load -- + * put it into a register then + * worry what to do with it. + */ + if(f->op == ONAME || f->op == OINDREG || f->op == OIND) { + switch(ft) { + default: + if(ewidth[ft] == 4){ + if(typeu[ft]) + a = AMOVWZ; + else + a = AMOVW; + }else + a = AMOVD; + break; + case TINT: + a = AMOVW; + break; + case TUINT: + a = AMOVWZ; + break; + case TFLOAT: + a = AFMOVS; + break; + case TDOUBLE: + a = AFMOVD; + break; + case TCHAR: + a = AMOVB; + break; + case TUCHAR: + a = AMOVBZ; + break; + case TSHORT: + a = AMOVH; + break; + case TUSHORT: + a = AMOVHZ; + break; + } + regalloc(&nod, f, t); + gins(a, f, &nod); + gmove(&nod, t); + regfree(&nod); + return; + } + + /* + * a store -- + * put it into a register then + * store it. + */ + if(t->op == ONAME || t->op == OINDREG || t->op == OIND) { + switch(tt) { + default: + if(ewidth[tt] == 4) + a = AMOVW; + else + a = AMOVD; + break; + case TINT: + a = AMOVW; + break; + case TUINT: + a = AMOVWZ; + break; + case TUCHAR: + a = AMOVBZ; + break; + case TCHAR: + a = AMOVB; + break; + case TUSHORT: + a = AMOVHZ; + break; + case TSHORT: + a = AMOVH; + break; + case TFLOAT: + a = AFMOVS; + break; + case TDOUBLE: + a = AFMOVD; + break; + } + if(!typefd[ft] && vconst(f) == 0) { + gins(a, f, t); + return; + } + if(ft == tt) + regalloc(&nod, t, f); + else + regalloc(&nod, t, Z); + gmove(f, &nod); + gins(a, &nod, t); + regfree(&nod); + return; + } + + /* + * type x type cross table + */ + a = AGOK; + switch(ft) { + case TDOUBLE: + case TFLOAT: + switch(tt) { + case TDOUBLE: + a = AFMOVD; + if(ft == TFLOAT) + a = AFMOVS; /* AFMOVSD */ + break; + case TFLOAT: + a = AFRSP; + if(ft == TFLOAT) + a = AFMOVS; + break; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TIND: + case TSHORT: + case TUSHORT: + case TCHAR: + case TUCHAR: + /* BUG: not right for unsigned int32 */ + regalloc(&nod, f, Z); /* should be type float */ + regsalloc(&fxrat, f); + gins(AFCTIWZ, f, &nod); + gins(AFMOVD, &nod, &fxrat); + regfree(&nod); + fxrat.type = nodrat->type; + fxrat.etype = nodrat->etype; + fxrat.xoffset += 4; + gins(AMOVW, &fxrat, t); /* TO DO */ + gmove(t, t); + return; + case TVLONG: + case TUVLONG: + /* BUG: not right for unsigned int32 */ + regalloc(&nod, f, Z); /* should be type float */ + regsalloc(&fxrat, f); + gins(AFCTIDZ, f, &nod); + gins(AFMOVD, &nod, &fxrat); + regfree(&nod); + fxrat.type = nodrat->type; + fxrat.etype = nodrat->etype; + gins(AMOVD, &fxrat, t); + gmove(t, t); + return; + } + break; + case TINT: + case TUINT: + case TLONG: + case TULONG: + switch(tt) { + case TDOUBLE: + case TFLOAT: + goto fxtofl; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TSHORT: + case TUSHORT: + case TCHAR: + case TUCHAR: + if(typeu[tt]) + a = AMOVWZ; + else + a = AMOVW; + break; + case TVLONG: + case TUVLONG: + case TIND: + a = AMOVD; + break; + } + break; + case TVLONG: + case TUVLONG: + case TIND: + switch(tt) { + case TDOUBLE: + case TFLOAT: + goto fxtofl; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + case TSHORT: + case TUSHORT: + case TCHAR: + case TUCHAR: + a = AMOVD; /* TO DO: conversion done? */ + break; + } + break; + case TSHORT: + switch(tt) { + case TDOUBLE: + case TFLOAT: + goto fxtofl; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + a = AMOVH; + break; + case TSHORT: + case TUSHORT: + case TCHAR: + case TUCHAR: + a = AMOVD; + break; + } + break; + case TUSHORT: + switch(tt) { + case TDOUBLE: + case TFLOAT: + goto fxtofl; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + a = AMOVHZ; + break; + case TSHORT: + case TUSHORT: + case TCHAR: + case TUCHAR: + a = AMOVD; + break; + } + break; + case TCHAR: + switch(tt) { + case TDOUBLE: + case TFLOAT: + goto fxtofl; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + case TSHORT: + case TUSHORT: + a = AMOVB; + break; + case TCHAR: + case TUCHAR: + a = AMOVD; + break; + } + break; + case TUCHAR: + switch(tt) { + case TDOUBLE: + case TFLOAT: + fxtofl: + /* + * rat[0] = 0x43300000; rat[1] = f^0x80000000; + * t = *(double*)rat - FREGCVI; + * is-unsigned(t) => if(t<0) t += 2^32; + * could be streamlined for int-to-float + */ + regalloc(&fxc0, f, Z); + regalloc(&fxc2, f, Z); + regsalloc(&fxrat, t); /* should be type float */ + gins(AMOVW, nodconst(0x43300000L), &fxc0); + gins(AMOVW, f, &fxc2); + gins(AXOR, nodconst(0x80000000L), &fxc2); + if(ctxt->arch->endian == BigEndian) { + gins(AMOVW, &fxc0, &fxrat); + fxc1 = fxrat; + fxc1.type = nodrat->type; + fxc1.etype = nodrat->etype; + fxc1.xoffset += SZ_LONG; + gins(AMOVW, &fxc2, &fxc1); + } else { + gins(AMOVW, &fxc2, &fxrat); + fxc1 = fxrat; + fxc1.type = nodrat->type; + fxc1.etype = nodrat->etype; + fxc1.xoffset += SZ_LONG; + gins(AMOVW, &fxc0, &fxc1); + } + regfree(&fxc2); + regfree(&fxc0); + regalloc(&nod, t, t); /* should be type float */ + gins(AFMOVD, &fxrat, &nod); + nodreg(&fxc1, t, NREG+FREGCVI); + gins(AFSUB, &fxc1, &nod); + a = AFMOVD; + if(tt == TFLOAT) + a = AFRSP; + gins(a, &nod, t); + regfree(&nod); + if(ft == TULONG) { + regalloc(&nod, t, Z); + if(tt == TFLOAT) { + gins(AFCMPU, t, Z); + p->to.type = D_FREG; + p->to.reg = FREGZERO; + gins(ABGE, Z, Z); + p1 = p; + gins(AFMOVS, nodfconst(4294967296.), &nod); + gins(AFADDS, &nod, t); + } else { + gins(AFCMPU, t, Z); + p->to.type = D_FREG; + p->to.reg = FREGZERO; + gins(ABGE, Z, Z); + p1 = p; + gins(AFMOVD, nodfconst(4294967296.), &nod); + gins(AFADD, &nod, t); + } + patch(p1, pc); + regfree(&nod); + } + return; + case TINT: + case TUINT: + case TLONG: + case TULONG: + case TVLONG: + case TUVLONG: + case TIND: + case TSHORT: + case TUSHORT: + a = AMOVBZ; + break; + case TCHAR: + case TUCHAR: + a = AMOVD; + break; + } + break; + } + if(a == AGOK) + diag(Z, "bad opcode in gmove %T -> %T", f->type, t->type); + if(a == AMOVD || (a == AMOVW || a == AMOVWZ) && ewidth[ft] == ewidth[tt] || a == AFMOVS || a == AFMOVD) + if(samaddr(f, t)) + return; + gins(a, f, t); +} + +void +gins(int a, Node *f, Node *t) +{ + + nextpc(); + p->as = a; + if(f != Z) + naddr(f, &p->from); + if(t != Z) + naddr(t, &p->to); + if(debug['g']) + print("%P\n", p); +} + +void +gopcode(int o, Node *f1, Node *f2, Node *t) +{ + int a, et; + Addr ta; + int uns; + + uns = 0; + et = TLONG; + if(f1 != Z && f1->type != T) { + if(f1->op == OCONST && t != Z && t->type != T) + et = t->type->etype; + else + et = f1->type->etype; + } + a = AGOK; + switch(o) { + case OAS: + gmove(f1, t); + return; + + case OASADD: + case OADD: + a = AADD; + if(et == TFLOAT) + a = AFADDS; + else + if(et == TDOUBLE) + a = AFADD; + break; + + case OASSUB: + case OSUB: + a = ASUB; + if(et == TFLOAT) + a = AFSUBS; + else + if(et == TDOUBLE) + a = AFSUB; + break; + + case OASOR: + case OOR: + a = AOR; + break; + + case OASAND: + case OAND: + a = AAND; + if(f1->op == OCONST) + a = AANDCC; + break; + + case OASXOR: + case OXOR: + a = AXOR; + break; + + case OASLSHR: + case OLSHR: + a = ASRW; + if(isv(et)) + a = ASRD; + break; + + case OASASHR: + case OASHR: + a = ASRAW; + if(isv(et)) + a = ASRAD; + break; + + case OASASHL: + case OASHL: + a = ASLW; + if(isv(et)) + a = ASLD; + break; + + case OFUNC: + a = ABL; + break; + + case OASLMUL: + case OLMUL: + case OASMUL: + case OMUL: + if(et == TFLOAT) { + a = AFMULS; + break; + } else + if(et == TDOUBLE) { + a = AFMUL; + break; + } + a = AMULLW; + if(isv(et)) + a = AMULLD; + break; + + case OASDIV: + case ODIV: + if(et == TFLOAT) { + a = AFDIVS; + break; + } else + if(et == TDOUBLE) { + a = AFDIV; + break; + } else + a = ADIVW; + if(isv(et)) + a = ADIVD; + break; + + case OASMOD: + case OMOD: + a = AREM; + if(isv(et)) + a = AREMD; + break; + + case OASLMOD: + case OLMOD: + a = AREMU; + if(isv(et)) + a = AREMDU; + break; + + case OASLDIV: + case OLDIV: + a = ADIVWU; + if(isv(et)) + a = ADIVDU; + break; + + case OCOM: + a = ANOR; + break; + + case ONEG: + a = ANEG; + if(et == TFLOAT || et == TDOUBLE) + a = AFNEG; + break; + + case OEQ: + a = ABEQ; + goto cmp; + + case ONE: + a = ABNE; + goto cmp; + + case OLT: + a = ABLT; + goto cmp; + + case OLE: + a = ABLE; + goto cmp; + + case OGE: + a = ABGE; + goto cmp; + + case OGT: + a = ABGT; + goto cmp; + + case OLO: + a = ABLT; + goto cmpu; + + case OLS: + a = ABLE; + goto cmpu; + + case OHS: + a = ABGE; + goto cmpu; + + case OHI: + a = ABGT; + goto cmpu; + + cmpu: + uns = 1; + cmp: + nextpc(); + switch(et){ + case TINT: + case TLONG: + p->as = ACMPW; + break; + case TUINT: + case TULONG: + p->as = ACMPWU; + break; + case TFLOAT: + case TDOUBLE: + p->as = AFCMPU; + break; + default: + p->as = uns? ACMPU: ACMP; + break; + } + if(f1 != Z) + naddr(f1, &p->from); + if(t != Z) + naddr(t, &p->to); + if(f1 == Z || t == Z || f2 != Z) + diag(Z, "bad cmp in gopcode %O", o); + if(debug['g']) + print("%P\n", p); + f1 = Z; + f2 = Z; + t = Z; + break; + } + if(a == AGOK) + diag(Z, "bad in gopcode %O", o); + nextpc(); + p->as = a; + if(f1 != Z) + naddr(f1, &p->from); + if(f2 != Z) { + naddr(f2, &ta); + p->reg = ta.reg; + if(ta.type == D_CONST && ta.offset == 0) { + if(R0ISZERO) + p->reg = REGZERO; + else + diag(Z, "REGZERO in gopcode %O", o); + } + } + if(t != Z) + naddr(t, &p->to); + if(debug['g']) + print("%P\n", p); +} + +int +samaddr(Node *f, Node *t) +{ + return f->op == OREGISTER && t->op == OREGISTER && f->reg == t->reg; +} + +void +gbranch(int o) +{ + int a; + + a = AGOK; + switch(o) { + case ORETURN: + a = ARETURN; + break; + case OGOTO: + a = ABR; + break; + } + nextpc(); + if(a == AGOK) { + diag(Z, "bad in gbranch %O", o); + nextpc(); + } + p->as = a; +} + +void +patch(Prog *op, int32 pc) +{ + + op->to.offset = pc; + op->to.type = D_BRANCH; +} + +void +gpseudo(int a, Sym *s, Node *n) +{ + + nextpc(); + p->as = a; + p->from.type = D_OREG; + p->from.sym = linksym(s); + + switch(a) { + case ATEXT: + p->reg = textflag; + textflag = 0; + break; + case AGLOBL: + p->reg = s->dataflag; + break; + } + + p->from.name = D_EXTERN; + if(s->class == CSTATIC) + p->from.name = D_STATIC; + naddr(n, &p->to); + if(a == ADATA || a == AGLOBL) + pc--; +} + +int +sval(int32 v) +{ + + if(v >= -(1<<15) && v < (1<<15)) + return 1; + return 0; +} + +void +gpcdata(int index, int value) +{ + Node n1; + + n1 = *nodconst(index); + gins(APCDATA, &n1, nodconst(value)); +} + +void +gprefetch(Node *n) +{ + // TODO(minux) + USED(n); + /* + Node n1; + + regalloc(&n1, n, Z); + gmove(n, &n1); + n1.op = OINDREG; + gins(ADCBT, &n1, Z); + regfree(&n1); + */ +} + + +int +sconst(Node *n) +{ + vlong vv; + + if(n->op == OCONST) { + if(!typefd[n->type->etype]) { + vv = n->vconst; + if(vv >= -(((vlong)1)<<15) && vv < (((vlong)1)<<15)) + return 1; + } + } + return 0; +} + +int +uconst(Node *n) +{ + vlong vv; + + if(n->op == OCONST) { + if(!typefd[n->type->etype]) { + vv = n->vconst; + if(vv >= 0 && vv < (((vlong)1)<<16)) + return 1; + } + } + return 0; +} + +int +immconst(Node *n) +{ + vlong v; + + if(n->op != OCONST || typefd[n->type->etype]) + return 0; + v = n->vconst; + if((v & 0xFFFF) == 0) + v >>= 16; + if(v >= 0 && v < ((vlong)1<<16)) + return 1; + if(v >= -((vlong)1<<15) && v <= ((vlong)1<<15)) + return 1; + return 0; +} + +int32 +exreg(Type *t) +{ + int32 o; + + if(typechlpv[t->etype]) { + if(exregoffset <= 3) + return 0; + o = exregoffset; + exregoffset--; + return o; + } + if(typefd[t->etype]) { + if(exfregoffset <= 16) + return 0; + o = exfregoffset + NREG; + exfregoffset--; + return o; + } + return 0; +} + +schar ewidth[NTYPE] = +{ + -1, /* [TXXX] */ + SZ_CHAR, /* [TCHAR] */ + SZ_CHAR, /* [TUCHAR] */ + SZ_SHORT, /* [TSHORT] */ + SZ_SHORT, /* [TUSHORT] */ + SZ_INT, /* [TINT] */ + SZ_INT, /* [TUINT] */ + SZ_LONG, /* [TLONG] */ + SZ_LONG, /* [TULONG] */ + SZ_VLONG, /* [TVLONG] */ + SZ_VLONG, /* [TUVLONG] */ + SZ_FLOAT, /* [TFLOAT] */ + SZ_DOUBLE, /* [TDOUBLE] */ + SZ_IND, /* [TIND] */ + 0, /* [TFUNC] */ + -1, /* [TARRAY] */ + 0, /* [TVOID] */ + -1, /* [TSTRUCT] */ + -1, /* [TUNION] */ + SZ_INT, /* [TENUM] */ +}; +int32 ncast[NTYPE] = +{ + 0, /* [TXXX] */ + BCHAR|BUCHAR, /* [TCHAR] */ + BCHAR|BUCHAR, /* [TUCHAR] */ + BSHORT|BUSHORT, /* [TSHORT] */ + BSHORT|BUSHORT, /* [TUSHORT] */ + BINT|BUINT|BLONG|BULONG, /* [TINT] */ + BINT|BUINT|BLONG|BULONG, /* [TUINT] */ + BINT|BUINT|BLONG|BULONG, /* [TLONG] */ + BINT|BUINT|BLONG|BULONG, /* [TULONG] */ + BVLONG|BUVLONG|BIND, /* [TVLONG] */ + BVLONG|BUVLONG|BIND, /* [TUVLONG] */ + BFLOAT, /* [TFLOAT] */ + BDOUBLE, /* [TDOUBLE] */ + BVLONG|BUVLONG|BIND, /* [TIND] */ + 0, /* [TFUNC] */ + 0, /* [TARRAY] */ + 0, /* [TVOID] */ + BSTRUCT, /* [TSTRUCT] */ + BUNION, /* [TUNION] */ + 0, /* [TENUM] */ +}; diff --cc src/cmd/9g/cgen.c index 86a56975ad,0000000000..e389360016 mode 100644,000000..100644 --- a/src/cmd/9g/cgen.c +++ b/src/cmd/9g/cgen.c @@@ -1,1764 -1,0 +1,1763 @@@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include "gg.h" + +/* + * generate: + * res = n; + * simplifies and calls gmove. + */ +void +cgen(Node *n, Node *res) +{ + Node *nl, *nr, *r; + Node n1, n2; + int a, f; + Prog *p1, *p2, *p3; + Addr addr; + +//print("cgen %N(%d) -> %N(%d)\n", n, n->addable, res, res->addable); + if(debug['g']) { + dump("\ncgen-n", n); + dump("cgen-res", res); + } + if(n == N || n->type == T) + goto ret; + + if(res == N || res->type == T) + fatal("cgen: res nil"); + + while(n->op == OCONVNOP) + n = n->left; + + switch(n->op) { + case OSLICE: + case OSLICEARR: + case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_slice(n, &n1); + cgen(&n1, res); + } else + cgen_slice(n, res); + goto ret; + case OEFACE: + if (res->op != ONAME || !res->addable) { + tempname(&n1, n->type); + cgen_eface(n, &n1); + cgen(&n1, res); + } else + cgen_eface(n, res); + goto ret; + } + + if(n->ullman >= UINF) { + if(n->op == OINDREG) + fatal("cgen: this is going to misscompile"); + if(res->ullman >= UINF) { + tempname(&n1, n->type); + cgen(n, &n1); + cgen(&n1, res); + goto ret; + } + } + + if(isfat(n->type)) { + if(n->type->width < 0) + fatal("forgot to compute width for %T", n->type); + sgen(n, res, n->type->width); + goto ret; + } + + if(!res->addable) { + 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; + } + + if(res->ullman >= UINF) + goto gen; + + if(complexop(n, res)) { + complexgen(n, res); + goto ret; + } + + f = 1; // gen thru register + switch(n->op) { + case OLITERAL: + if(smallintconst(n)) + f = 0; + break; + case OREGISTER: + f = 0; + break; + } + + if(!iscomplex[n->type->etype]) { + a = optoas(OAS, res->type); + if(sudoaddable(a, res, &addr)) { + if(f) { + regalloc(&n2, res->type, N); + cgen(n, &n2); + p1 = gins(a, &n2, N); + regfree(&n2); + } else + p1 = gins(a, n, N); + p1->to = addr; + if(debug['g']) + print("%P [ignore previous line]\n", p1); + sudoclean(); + goto ret; + } + } + + gen: + igen(res, &n1, N); + cgen(n, &n1); + regfree(&n1); + goto ret; + } + + // update addressability for string, slice + // can't do in walk because n->left->addable + // changes if n->left is an escaping local variable. + switch(n->op) { + case OSPTR: + case OLEN: + if(isslice(n->left->type) || istype(n->left->type, TSTRING)) + n->addable = n->left->addable; + break; + case OCAP: + if(isslice(n->left->type)) + n->addable = n->left->addable; + break; + case OITAB: + n->addable = n->left->addable; + break; + } + + if(complexop(n, res)) { + complexgen(n, res); + goto ret; + } + + // if both are addressable, move + if(n->addable) { + if(n->op == OREGISTER || res->op == OREGISTER) { + gmove(n, res); + } else { + regalloc(&n1, n->type, N); + gmove(n, &n1); + cgen(&n1, res); + regfree(&n1); + } + goto ret; + } + + nl = n->left; + nr = n->right; + + if(nl != N && nl->ullman >= UINF) + if(nr != N && nr->ullman >= UINF) { + tempname(&n1, nl->type); + cgen(nl, &n1); + n2 = *n; + n2.left = &n1; + cgen(&n2, res); + goto ret; + } + + if(!iscomplex[n->type->etype]) { + a = optoas(OAS, n->type); + if(sudoaddable(a, n, &addr)) { + if(res->op == OREGISTER) { + p1 = gins(a, N, res); + p1->from = addr; + } else { + regalloc(&n2, n->type, N); + p1 = gins(a, N, &n2); + p1->from = addr; + gins(a, &n2, res); + regfree(&n2); + } + sudoclean(); + goto ret; + } + } + + // TODO(minux): we shouldn't reverse FP comparisons, but then we need to synthesize + // OGE, OLE, and ONE ourselves. + // if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype]) goto flt; + + switch(n->op) { + default: + dump("cgen", n); + fatal("cgen: unknown op %+hN", n); + break; + + // these call bgen to get a bool value + case OOROR: + case OANDAND: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + case OGT: + case ONOT: + p1 = gbranch(ABR, T, 0); + p2 = pc; + gmove(nodbool(1), res); + p3 = gbranch(ABR, T, 0); + patch(p1, pc); + bgen(n, 1, 0, p2); + gmove(nodbool(0), res); + patch(p3, pc); + goto ret; + + case OPLUS: + cgen(nl, res); + goto ret; + + // unary + case OCOM: + a = optoas(OXOR, nl->type); + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + nodconst(&n2, nl->type, -1); + gins(a, &n2, &n1); + gmove(&n1, res); + regfree(&n1); + goto ret; + + case OMINUS: + if(isfloat[nl->type->etype]) { + nr = nodintconst(-1); + convlit(&nr, n->type); + a = optoas(OMUL, nl->type); + goto sbop; + } + a = optoas(n->op, nl->type); + goto uop; + + // symmetric binary + case OAND: + case OOR: + case OXOR: + case OADD: - case OADDPTR: + case OMUL: + a = optoas(n->op, nl->type); + goto sbop; + + // asymmetric binary + case OSUB: + a = optoas(n->op, nl->type); + goto abop; + + case OHMUL: + cgen_hmul(nl, nr, res); + break; + + case OCONV: + if(n->type->width > nl->type->width) { + // If loading from memory, do conversion during load, + // so as to avoid use of 8-bit register in, say, int(*byteptr). + switch(nl->op) { + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: + igen(nl, &n1, res); + regalloc(&n2, n->type, res); + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n2); + regfree(&n1); + goto ret; + } + } + + regalloc(&n1, nl->type, res); + regalloc(&n2, n->type, &n1); + cgen(nl, &n1); + + // if we do the conversion n1 -> n2 here + // reusing the register, then gmove won't + // have to allocate its own register. + gmove(&n1, &n2); + gmove(&n2, res); + regfree(&n2); + regfree(&n1); + break; + + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: // PHEAP or PPARAMREF var + igen(n, &n1, res); + gmove(&n1, res); + regfree(&n1); + break; + + case OITAB: + // interface table is first word of interface value + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; + + case OSPTR: + // pointer is the first word of string or slice. + if(isconst(nl, CTSTR)) { + regalloc(&n1, types[tptr], res); + p1 = gins(AMOVD, N, &n1); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + gmove(&n1, res); + regfree(&n1); + break; + } + igen(nl, &n1, res); + n1.type = n->type; + gmove(&n1, res); + regfree(&n1); + break; + + case OLEN: + if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) { + // map and chan have len in the first int-sized word. + // a zero pointer means zero length + regalloc(&n1, types[tptr], res); + cgen(nl, &n1); + + nodconst(&n2, types[tptr], 0); + gins(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); + + n2 = n1; + n2.op = OINDREG; + n2.type = types[simtype[TINT]]; + gmove(&n2, &n1); + + patch(p1, pc); + + gmove(&n1, res); + regfree(&n1); + break; + } + if(istype(nl->type, TSTRING) || isslice(nl->type)) { + // both slice and string have len one pointer into the struct. + // a zero pointer means zero length + igen(nl, &n1, res); + n1.type = types[simtype[TUINT]]; + n1.xoffset += Array_nel; + gmove(&n1, res); + regfree(&n1); + break; + } + fatal("cgen: OLEN: unknown type %lT", nl->type); + break; + + case OCAP: + if(istype(nl->type, TCHAN)) { + // chan has cap in the second int-sized word. + // a zero pointer means zero length + regalloc(&n1, types[tptr], res); + cgen(nl, &n1); + + nodconst(&n2, types[tptr], 0); + gins(optoas(OCMP, types[tptr]), &n1, &n2); + p1 = gbranch(optoas(OEQ, types[tptr]), T, 0); + + n2 = n1; + n2.op = OINDREG; + n2.xoffset = widthint; + n2.type = types[simtype[TINT]]; + gmove(&n2, &n1); + + patch(p1, pc); + + gmove(&n1, res); + regfree(&n1); + break; + } + if(isslice(nl->type)) { + igen(nl, &n1, res); + n1.type = types[simtype[TUINT]]; + n1.xoffset += Array_cap; + gmove(&n1, res); + regfree(&n1); + break; + } + fatal("cgen: OCAP: unknown type %lT", nl->type); + break; + + case OADDR: + if(n->bounded) // let race detector avoid nil checks + disable_checknil++; + agen(nl, res); + if(n->bounded) + disable_checknil--; + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + cgen_callret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_callret(n, res); + break; + + case OCALLFUNC: + cgen_call(n, 0); + cgen_callret(n, res); + break; + + case OMOD: + case ODIV: + if(isfloat[n->type->etype]) { + a = optoas(n->op, nl->type); + goto abop; + } + + if(nl->ullman >= nr->ullman) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + cgen_div(n->op, &n1, nr, res); + regfree(&n1); + } else { + if(!smallintconst(nr)) { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + } else { + n2 = *nr; + } + cgen_div(n->op, nl, &n2, res); + if(n2.op != OLITERAL) + regfree(&n2); + } + break; + + case OLSH: + case ORSH: + case OLROT: + cgen_shift(n->op, n->bounded, nl, nr, res); + break; + } + goto ret; + +sbop: // symmetric binary + /* + * put simplest on right - we'll generate into left + * and then adjust it using the computation of right. + * constants and variables have the same ullman + * count, so look for constants specially. + * + * an integer constant we can use as an immediate + * is simpler than a variable - we can use the immediate + * in the adjustment instruction directly - so it goes + * on the right. + * + * other constants, like big integers or floating point + * constants, require a mov into a register, so those + * might as well go on the left, so we can reuse that + * register for the computation. + */ + if(nl->ullman < nr->ullman || + (nl->ullman == nr->ullman && + (smallintconst(nl) || (nr->op == OLITERAL && !smallintconst(nr))))) { + r = nl; + nl = nr; + nr = r; + } + +abop: // asymmetric binary + if(nl->ullman >= nr->ullman) { + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + /* + * This generates smaller code - it avoids a MOV - but it's + * easily 10% slower due to not being able to + * optimize/manipulate the move. + * To see, run: go test -bench . crypto/md5 + * with and without. + * + if(sudoaddable(a, nr, &addr)) { + p1 = gins(a, N, &n1); + p1->from = addr; + gmove(&n1, res); + sudoclean(); + regfree(&n1); + goto ret; + } + * + */ + // TODO(minux): enable using constants directly in certain instructions. + //if(smallintconst(nr)) + // n2 = *nr; + //else { + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + //} + } else { + //if(smallintconst(nr)) + // n2 = *nr; + //else { + regalloc(&n2, nr->type, res); + cgen(nr, &n2); + //} + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + } + gins(a, &n2, &n1); + // Normalize result for types smaller than word. + if(n->type->width < widthreg) { + switch(n->op) { + case OADD: + case OSUB: + case OMUL: + case OLSH: + gins(optoas(OAS, n->type), &n1, &n1); + break; + } + } + gmove(&n1, res); + regfree(&n1); + if(n2.op != OLITERAL) + regfree(&n2); + goto ret; + +uop: // unary + regalloc(&n1, nl->type, res); + cgen(nl, &n1); + gins(a, N, &n1); + gmove(&n1, res); + regfree(&n1); + goto ret; + +ret: + ; +} + +/* + * allocate a register (reusing res if possible) and generate + * a = n + * The caller must call regfree(a). + */ +void +cgenr(Node *n, Node *a, Node *res) +{ + Node n1; + + if(debug['g']) + dump("cgenr-n", n); + + if(isfat(n->type)) + fatal("cgenr on fat node"); + + if(n->addable) { + regalloc(a, n->type, res); + gmove(n, a); + return; + } + + switch(n->op) { + case ONAME: + case ODOT: + case ODOTPTR: + case OINDEX: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + gmove(&n1, a); + regfree(&n1); + break; + default: + regalloc(a, n->type, res); + cgen(n, a); + break; + } +} + +/* + * allocate a register (reusing res if possible) and generate + * a = &n + * The caller must call regfree(a). + * The generated code checks that the result is not nil. + */ +void +agenr(Node *n, Node *a, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3, n4, tmp; + Prog *p1, *p2; + uint32 w; + uint64 v; + + if(debug['g']) + dump("agenr-n", n); + + nl = n->left; + nr = n->right; + + switch(n->op) { + case ODOT: + case ODOTPTR: + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + igen(n, &n1, res); + regalloc(a, types[tptr], &n1); + agen(&n1, a); + regfree(&n1); + break; + + case OIND: + cgenr(n->left, a, res); + cgen_checknil(a); + break; + + case OINDEX: + p2 = nil; // to be patched to panicindex. + w = n->type->width; + //bounded = debug['B'] || n->bounded; + if(nr->addable) { + if(!isconst(nr, CTINT)) + tempname(&tmp, types[TINT64]); + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + if(!isconst(nr, CTINT)) { + cgen(nr, &tmp); + regalloc(&n1, tmp.type, N); + gmove(&tmp, &n1); + } + } else if(nl->addable) { + if(!isconst(nr, CTINT)) { + tempname(&tmp, types[TINT64]); + cgen(nr, &tmp); + regalloc(&n1, tmp.type, N); + gmove(&tmp, &n1); + } + if(!isconst(nl, CTSTR)) { + agenr(nl, &n3, res); + } + } else { + tempname(&tmp, types[TINT64]); + cgen(nr, &tmp); + nr = &tmp; + if(!isconst(nl, CTSTR)) + agenr(nl, &n3, res); + regalloc(&n1, tmp.type, N); + gins(optoas(OAS, tmp.type), &tmp, &n1); + } + + // &a is in &n3 (allocated in res) + // i is in &n1 (if not constant) + // w is width + + // constant index + if(isconst(nr, CTINT)) { + if(isconst(nl, CTSTR)) + fatal("constant string constant index"); + v = mpgetfix(nr->val.u.xval); + if(isslice(nl->type) || nl->type->etype == TSTRING) { + if(!debug['B'] && !n->bounded) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + regalloc(&n4, n1.type, N); + gmove(&n1, &n4); + ginscon2(optoas(OCMP, types[TUINT64]), &n4, v); + regfree(&n4); + p1 = gbranch(optoas(OGT, types[TUINT64]), T, +1); + ginscall(panicindex, 0); + patch(p1, pc); + } + + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } + + if (v*w != 0) { + ginscon(optoas(OADD, types[tptr]), v*w, &n3); + } + *a = n3; + break; + } + + regalloc(&n2, types[TINT64], &n1); // i + gmove(&n1, &n2); + regfree(&n1); + + if(!debug['B'] && !n->bounded) { + // check bounds + if(isconst(nl, CTSTR)) { + nodconst(&n4, types[TUINT64], nl->val.u.sval->len); + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_nel; + regalloc(&n4, types[TUINT64], N); + gmove(&n1, &n4); + } else { + if(nl->type->bound < (1<<15)-1) + nodconst(&n4, types[TUINT64], nl->type->bound); + else { + regalloc(&n4, types[TUINT64], N); + p1 = gins(AMOVD, N, &n4); + p1->from.type = D_CONST; + p1->from.offset = nl->type->bound; + } + } + gins(optoas(OCMP, types[TUINT64]), &n2, &n4); + if(n4.op == OREGISTER) + regfree(&n4); + p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1); + if(p2) + patch(p2, pc); + ginscall(panicindex, 0); + patch(p1, pc); + } + + if(isconst(nl, CTSTR)) { + regalloc(&n3, types[tptr], res); + p1 = gins(AMOVD, N, &n3); + datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); + p1->from.type = D_CONST; + } else if(isslice(nl->type) || nl->type->etype == TSTRING) { + n1 = n3; + n1.op = OINDREG; + n1.type = types[tptr]; + n1.xoffset = Array_array; + gmove(&n1, &n3); + } + + if(w == 0) { + // nothing to do + } else if(w == 1) { + /* w already scaled */ + gins(optoas(OADD, types[tptr]), &n2, &n3); + } /* else if(w == 2 || w == 4 || w == 8) { + // TODO(minux): scale using shift + } */ else { + regalloc(&n4, types[TUINT64], N); + nodconst(&n1, types[TUINT64], w); + gmove(&n1, &n4); + gins(optoas(OMUL, types[TUINT64]), &n4, &n2); + gins(optoas(OADD, types[tptr]), &n2, &n3); + regfree(&n4); + } + + *a = n3; + regfree(&n2); + break; + + default: + regalloc(a, types[tptr], res); + agen(n, a); + break; + } +} + +static void +ginsadd(int as, vlong off, Node *dst) +{ + Node n1; + + regalloc(&n1, types[tptr], dst); + gmove(dst, &n1); + ginscon(as, off, &n1); + gmove(&n1, dst); + regfree(&n1); +} + +/* + * generate: + * res = &n; + * The generated code checks that the result is not nil. + */ +void +agen(Node *n, Node *res) +{ + Node *nl, *nr; + Node n1, n2, n3; + + if(debug['g']) { + dump("\nagen-res", res); + dump("agen-r", n); + } + if(n == N || n->type == T) + return; + + while(n->op == OCONVNOP) + n = n->left; + + if(isconst(n, CTNIL) && n->type->width > widthptr) { + // Use of a nil interface or nil slice. + // Create a temporary we can take the address of and read. + // The generated code is just going to panic, so it need not + // be terribly efficient. See issue 3670. + tempname(&n1, n->type); + gvardef(&n1); + clearfat(&n1); + regalloc(&n2, types[tptr], res); + memset(&n3, 0, sizeof n3); + n3.op = OADDR; + n3.left = &n1; + gins(AMOVD, &n3, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + if(n->addable) { + memset(&n1, 0, sizeof n1); + n1.op = OADDR; + n1.left = n; + regalloc(&n2, types[tptr], res); + gins(AMOVD, &n1, &n2); + gmove(&n2, res); + regfree(&n2); + goto ret; + } + + nl = n->left; + nr = n->right; + USED(nr); + + switch(n->op) { + default: + fatal("agen: unknown op %+hN", n); + break; + + case OCALLMETH: + // TODO(minux): 5g has this: Release res so that it is available for cgen_call. + // Pick it up again after the call for OCALLMETH and OCALLFUNC. + cgen_callmeth(n, 0); + cgen_aret(n, res); + break; + + case OCALLINTER: + cgen_callinter(n, res, 0); + cgen_aret(n, res); + break; + + case OCALLFUNC: + cgen_call(n, 0); + cgen_aret(n, res); + break; + + case OSLICE: + case OSLICEARR: + case OSLICESTR: + case OSLICE3: + case OSLICE3ARR: + tempname(&n1, n->type); + cgen_slice(n, &n1); + agen(&n1, res); + break; + + case OEFACE: + tempname(&n1, n->type); + cgen_eface(n, &n1); + agen(&n1, res); + break; + + case OINDEX: + agenr(n, &n1, res); + gmove(&n1, res); + regfree(&n1); + break; + + case ONAME: + // should only get here with names in this func. + if(n->funcdepth > 0 && n->funcdepth != funcdepth) { + dump("bad agen", n); + fatal("agen: bad ONAME funcdepth %d != %d", + n->funcdepth, funcdepth); + } + + // should only get here for heap vars or paramref + if(!(n->class & PHEAP) && n->class != PPARAMREF) { + dump("bad agen", n); + fatal("agen: bad ONAME class %#x", n->class); + } + cgen(n->heapaddr, res); + if(n->xoffset != 0) { + ginsadd(optoas(OADD, types[tptr]), n->xoffset, res); + } + break; + + case OIND: + cgen(nl, res); + cgen_checknil(res); + break; + + case ODOT: + agen(nl, res); + if(n->xoffset != 0) { + ginsadd(optoas(OADD, types[tptr]), n->xoffset, res); + } + break; + + case ODOTPTR: + cgen(nl, res); + cgen_checknil(res); + if(n->xoffset != 0) { + ginsadd(optoas(OADD, types[tptr]), n->xoffset, res); + } + break; + } + +ret: + ; +} + +/* + * generate: + * newreg = &n; + * res = newreg + * + * on exit, a has been changed to be *newreg. + * caller must regfree(a). + * The generated code checks that the result is not *nil. + */ +void +igen(Node *n, Node *a, Node *res) +{ + Type *fp; + Iter flist; + Node n1; + + if(debug['g']) { + dump("\nigen-n", n); + } + switch(n->op) { + case ONAME: + if((n->class&PHEAP) || n->class == PPARAMREF) + break; + *a = *n; + return; + + case OINDREG: + // Increase the refcount of the register so that igen's caller + // has to call regfree. + if(n->val.u.reg != D_R0+REGSP) + reg[n->val.u.reg]++; + *a = *n; + return; + + case ODOT: + igen(n->left, a, res); + a->xoffset += n->xoffset; + a->type = n->type; + fixlargeoffset(a); + return; + + case ODOTPTR: + cgenr(n->left, a, res); + cgen_checknil(a); + a->op = OINDREG; + a->xoffset += n->xoffset; + a->type = n->type; + fixlargeoffset(a); + return; + + case OCALLFUNC: + case OCALLMETH: + case OCALLINTER: + switch(n->op) { + case OCALLFUNC: + cgen_call(n, 0); + break; + case OCALLMETH: + cgen_callmeth(n, 0); + break; + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + } + fp = structfirst(&flist, getoutarg(n->left->type)); + memset(a, 0, sizeof *a); + a->op = OINDREG; + a->val.u.reg = D_R0+REGSP; + a->addable = 1; + a->xoffset = fp->width + widthptr; // +widthptr: saved lr at 0(SP) + a->type = n->type; + return; + + case OINDEX: + // Index of fixed-size array by constant can + // put the offset in the addressing. + // Could do the same for slice except that we need + // to use the real index for the bounds checking. + if(isfixedarray(n->left->type) || + (isptr[n->left->type->etype] && isfixedarray(n->left->left->type))) + if(isconst(n->right, CTINT)) { + // Compute &a. + if(!isptr[n->left->type->etype]) + igen(n->left, a, res); + else { + igen(n->left, &n1, res); + cgen_checknil(&n1); + regalloc(a, types[tptr], res); + gmove(&n1, a); + regfree(&n1); + a->op = OINDREG; + } + + // Compute &a[i] as &a + i*width. + a->type = n->type; + a->xoffset += mpgetfix(n->right->val.u.xval)*n->type->width; + fixlargeoffset(a); + return; + } + break; + } + + agenr(n, a, res); + a->op = OINDREG; + a->type = n->type; +} + +/* + * generate: + * if(n == true) goto to; + */ +void +bgen(Node *n, int true, int likely, Prog *to) +{ + int et, a; + Node *nl, *nr, *l, *r; + Node n1, n2, tmp; + NodeList *ll; + Prog *p1, *p2; + + if(debug['g']) { + dump("\nbgen", n); + } + + if(n == N) + n = nodbool(1); + + if(n->ninit != nil) + genlist(n->ninit); + + if(n->type == T) { + convlit(&n, types[TBOOL]); + if(n->type == T) + goto ret; + } + + et = n->type->etype; + if(et != TBOOL) { + yyerror("cgen: bad type %T for %O", n->type, n->op); + patch(gins(AEND, N, N), to); + goto ret; + } + nr = N; + + while(n->op == OCONVNOP) { + n = n->left; + if(n->ninit != nil) + genlist(n->ninit); + } + + switch(n->op) { + default: + regalloc(&n1, n->type, N); + cgen(n, &n1); + nodconst(&n2, n->type, 0); + gins(optoas(OCMP, n->type), &n1, &n2); + a = ABNE; + if(!true) + a = ABEQ; + patch(gbranch(a, n->type, likely), to); + regfree(&n1); + goto ret; + + case OLITERAL: + // need to ask if it is bool? + if(!true == !n->val.u.bval) + patch(gbranch(ABR, T, likely), 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); + goto ret; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + nr = n->right; + if(nr == N || nr->type == T) + goto ret; + + case ONOT: // unary + nl = n->left; + if(nl == N || nl->type == T) + goto ret; + break; + } + + switch(n->op) { + + case ONOT: + bgen(nl, !true, likely, to); + goto ret; + + case OEQ: + case ONE: + case OLT: + case OGT: + case OLE: + case OGE: + a = n->op; + if(!true) { + if(isfloat[nr->type->etype]) { + // brcom is not valid on floats when NaN is involved. + p1 = gbranch(ABR, T, 0); + p2 = gbranch(ABR, T, 0); + patch(p1, pc); + ll = n->ninit; // avoid re-genning ninit + n->ninit = nil; + bgen(n, 1, -likely, p2); + n->ninit = ll; + patch(gbranch(ABR, T, 0), to); + patch(p2, pc); + goto ret; + } + a = brcom(a); + true = !true; + } + + // make simplest on right + if(nl->op == OLITERAL || (nl->ullman < nr->ullman && nl->ullman < UINF)) { + a = brrev(a); + r = nl; + nl = nr; + nr = r; + } + + if(isslice(nl->type)) { + // front end should only leave cmp to literal nil + if((a != OEQ && a != ONE) || nr->op != OLITERAL) { + yyerror("illegal slice comparison"); + break; + } + a = optoas(a, types[tptr]); + igen(nl, &n1, N); + n1.xoffset += Array_array; + n1.type = types[tptr]; + nodconst(&tmp, types[tptr], 0); + regalloc(&n2, types[tptr], &n1); + gmove(&n1, &n2); + gins(optoas(OCMP, types[tptr]), &n2, &tmp); + regfree(&n2); + patch(gbranch(a, types[tptr], likely), to); + regfree(&n1); + break; + } + + if(isinter(nl->type)) { + // front end should only leave cmp to literal nil + if((a != OEQ && a != ONE) || nr->op != OLITERAL) { + yyerror("illegal interface comparison"); + break; + } + a = optoas(a, types[tptr]); + igen(nl, &n1, N); + n1.type = types[tptr]; + nodconst(&tmp, types[tptr], 0); + regalloc(&n2, types[tptr], &n1); + gmove(&n1, &n2); + gins(optoas(OCMP, types[tptr]), &n2, &tmp); + regfree(&n2); + patch(gbranch(a, types[tptr], likely), to); + regfree(&n1); + break; + } + if(iscomplex[nl->type->etype]) { + complexbool(a, nl, nr, true, likely, to); + break; + } + + if(nr->ullman >= UINF) { + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + + tempname(&tmp, nl->type); + gmove(&n1, &tmp); + regfree(&n1); + + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + + regalloc(&n1, nl->type, N); + cgen(&tmp, &n1); + + goto cmp; + } + + regalloc(&n1, nl->type, N); + cgen(nl, &n1); + + // TODO(minux): cmpi does accept 16-bit signed immediate as p->to. + // and cmpli accepts 16-bit unsigned immediate. + //if(smallintconst(nr)) { + // gins(optoas(OCMP, nr->type), &n1, nr); + // patch(gbranch(optoas(a, nr->type), nr->type, likely), to); + // regfree(&n1); + // break; + //} + + regalloc(&n2, nr->type, N); + cgen(nr, &n2); + cmp: + l = &n1; + r = &n2; + gins(optoas(OCMP, nr->type), l, r); + if(isfloat[nr->type->etype] && (a == OLE || a == OGE)) { + // To get NaN right, must rewrite x <= y into separate x < y or x = y. + switch(a) { + case OLE: + a = OLT; + break; + case OGE: + a = OGT; + break; + } + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); + patch(gbranch(optoas(OEQ, nr->type), nr->type, likely), to); + } else { + patch(gbranch(optoas(a, nr->type), nr->type, likely), to); + } + regfree(&n1); + regfree(&n2); + break; + } + goto ret; + +ret: + ; +} + +/* + * n is on stack, either local variable + * or return value from function call. + * return n's offset from SP. + */ +int64 +stkof(Node *n) +{ + Type *t; + Iter flist; + int64 off; + + switch(n->op) { + case OINDREG: + return n->xoffset; + + case ODOT: + t = n->left->type; + if(isptr[t->etype]) + break; + off = stkof(n->left); + if(off == -1000 || off == 1000) + return off; + return off + n->xoffset; + + case OINDEX: + t = n->left->type; + if(!isfixedarray(t)) + break; + off = stkof(n->left); + if(off == -1000 || off == 1000) + return off; + if(isconst(n->right, CTINT)) + return off + t->type->width * mpgetfix(n->right->val.u.xval); + return 1000; + + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + + t = structfirst(&flist, getoutarg(t)); + if(t != T) + return t->width + widthptr; // +widthptr: correct for saved LR + break; + } + + // botch - probably failing to recognize address + // arithmetic on the above. eg INDEX and DOT + return -1000; +} + +/* + * block copy: + * memmove(&ns, &n, w); + */ +void +sgen(Node *n, Node *ns, int64 w) +{ + Node dst, src, tmp, nend; + int32 c, odst, osrc; + int dir, align, op; + Prog *p, *ploop; + NodeList *l; + Node *res = ns; + + if(debug['g']) { + print("\nsgen w=%lld\n", w); + dump("r", n); + dump("res", ns); + } + + if(n->ullman >= UINF && ns->ullman >= UINF) + fatal("sgen UINF"); + + if(w < 0) + fatal("sgen copy %lld", w); + + // If copying .args, that's all the results, so record definition sites + // for them for the liveness analysis. + if(ns->op == ONAME && strcmp(ns->sym->name, ".args") == 0) + for(l = curfn->dcl; l != nil; l = l->next) + if(l->n->class == PPARAMOUT) + gvardef(l->n); + + // Avoid taking the address for simple enough types. + //if(componentgen(n, ns)) + // return; + + if(w == 0) { + // evaluate side effects only. + regalloc(&dst, types[tptr], N); + agen(res, &dst); + agen(n, &dst); + regfree(&dst); + return; + } + + // determine alignment. + // want to avoid unaligned access, so have to use + // smaller operations for less aligned types. + // for example moving [4]byte must use 4 MOVB not 1 MOVW. + align = n->type->align; + switch(align) { + default: + fatal("sgen: invalid alignment %d for %T", align, n->type); + case 1: + op = AMOVBU; + break; + case 2: + op = AMOVHU; + break; + case 4: + op = AMOVWZU; // there is no lwau, only lwaux + break; + case 8: + op = AMOVDU; + break; + } + if(w%align) + fatal("sgen: unaligned size %lld (align=%d) for %T", w, align, n->type); + c = w / align; + + // offset on the stack + osrc = stkof(n); + odst = stkof(res); + if(osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000)) { + // osrc and odst both on stack, and at least one is in + // an unknown position. Could generate code to test + // for forward/backward copy, but instead just copy + // to a temporary location first. + tempname(&tmp, n->type); + sgen(n, &tmp, w); + sgen(&tmp, res, w); + return; + } + if(osrc%align != 0 || odst%align != 0) + fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align); + + // if we are copying forward on the stack and + // the src and dst overlap, then reverse direction + dir = align; + if(osrc < odst && odst < osrc+w) + dir = -dir; + + if(n->ullman >= res->ullman) { + agenr(n, &dst, res); // temporarily use dst + regalloc(&src, types[tptr], N); + gins(AMOVD, &dst, &src); + if(res->op == ONAME) + gvardef(res); + agen(res, &dst); + } else { + if(res->op == ONAME) + gvardef(res); + agenr(res, &dst, res); + agenr(n, &src, N); + } + + regalloc(&tmp, types[tptr], N); + + // set up end marker + memset(&nend, 0, sizeof nend); + + // move src and dest to the end of block if necessary + if(dir < 0) { + if(c >= 4) { + regalloc(&nend, types[tptr], N); + p = gins(AMOVD, &src, &nend); + } + + p = gins(AADD, N, &src); + p->from.type = D_CONST; + p->from.offset = w; + + p = gins(AADD, N, &dst); + p->from.type = D_CONST; + p->from.offset = w; + } else { + p = gins(AADD, N, &src); + p->from.type = D_CONST; + p->from.offset = -dir; + + p = gins(AADD, N, &dst); + p->from.type = D_CONST; + p->from.offset = -dir; + + if(c >= 4) { + regalloc(&nend, types[tptr], N); + p = gins(AMOVD, &src, &nend); + p->from.type = D_CONST; + p->from.offset = w; + } + } + + + // move + // TODO: enable duffcopy for larger copies. + if(c >= 4) { + p = gins(op, &src, &tmp); + p->from.type = D_OREG; + p->from.offset = dir; + ploop = p; + + p = gins(op, &tmp, &dst); + p->to.type = D_OREG; + p->to.offset = dir; + + p = gins(ACMP, &src, &nend); + + patch(gbranch(ABNE, T, 0), ploop); + regfree(&nend); + } else { + while(c-- > 0) { + p = gins(op, &src, &tmp); + p->from.type = D_OREG; + p->from.offset = dir; + + p = gins(op, &tmp, &dst); + p->to.type = D_OREG; + p->to.offset = dir; + } + } + + regfree(&dst); + regfree(&src); + regfree(&tmp); +} + +static int +cadable(Node *n) +{ + if(!n->addable) { + // dont know how it happens, + // but it does + return 0; + } + + switch(n->op) { + case ONAME: + return 1; + } + return 0; +} + +/* + * copy a composite value by moving its individual components. + * Slices, strings and interfaces are supported. + * 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; + Type *t; + int freel, freer; + vlong fldcount; + vlong loffset, roffset; + + freel = 0; + freer = 0; + + switch(nl->type->etype) { + default: + goto no; + + case TARRAY: + t = nl->type; + + // Slices are ok. + if(isslice(t)) + break; + // Small arrays are ok. + if(t->bound > 0 && t->bound <= 3 && !isfat(t->type)) + break; + + goto no; + + case TSTRUCT: + // Small structs with non-fat types are ok. + // Zero-sized structs are treated separately elsewhere. + fldcount = 0; + for(t=nl->type->type; t; t=t->down) { + if(isfat(t->type)) + goto no; + if(t->etype != TFIELD) + fatal("componentgen: not a TFIELD: %lT", t); + fldcount++; + } + if(fldcount == 0 || fldcount > 4) + goto no; + + break; + + case TSTRING: + case TINTER: + break; + } + + nodl = *nl; + if(!cadable(nl)) { + if(nr == N || !cadable(nr)) + goto no; + igen(nl, &nodl, N); + freel = 1; + } + + if(nr != N) { + nodr = *nr; + if(!cadable(nr)) { + igen(nr, &nodr, N); + freer = 1; + } + } + + // 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. + // (And also the assignments are useless.) + if(nr != N && nl->op == ONAME && nr->op == ONAME && nl == nr) + goto yes; + + switch(nl->type->etype) { + case TARRAY: + // componentgen for arrays. + if(nl->op == ONAME) + gvardef(nl); + t = nl->type; + if(!isslice(t)) { + nodl.type = t->type; + nodr.type = nodl.type; + for(fldcount=0; fldcount < t->bound; fldcount++) { + if(nr == N) + clearslim(&nodl); + else + gmove(&nodr, &nodl); + nodl.xoffset += t->type->width; + nodr.xoffset += t->type->width; + } + goto yes; + } + + // componentgen for slices. + nodl.xoffset += Array_array; + nodl.type = ptrto(nl->type->type); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_cap-Array_nel; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_cap-Array_nel; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRING: + if(nl->op == ONAME) + gvardef(nl); + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = types[simtype[TUINT]]; + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TINTER: + if(nl->op == ONAME) + gvardef(nl); + nodl.xoffset += Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + nodl.xoffset += Array_nel-Array_array; + nodl.type = ptrto(types[TUINT8]); + + if(nr != N) { + nodr.xoffset += Array_nel-Array_array; + nodr.type = nodl.type; + } else + nodconst(&nodr, nodl.type, 0); + gmove(&nodr, &nodl); + + goto yes; + + case TSTRUCT: + if(nl->op == ONAME) + gvardef(nl); + loffset = nodl.xoffset; + roffset = nodr.xoffset; + // funarg structs may not begin at offset zero. + if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type) + loffset -= nl->type->type->width; + if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type) + roffset -= nr->type->type->width; + + for(t=nl->type->type; t; t=t->down) { + nodl.xoffset = loffset + t->width; + nodl.type = t->type; + + if(nr == N) + clearslim(&nodl); + else { + nodr.xoffset = roffset + t->width; + nodr.type = nodl.type; + gmove(&nodr, &nodl); + } + } + goto yes; + } + +no: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 0; + +yes: + if(freer) + regfree(&nodr); + if(freel) + regfree(&nodl); + return 1; +} diff --cc src/cmd/9g/gg.h index 6aa00117f8,0000000000..2eb516b403 mode 100644,000000..100644 --- a/src/cmd/9g/gg.h +++ b/src/cmd/9g/gg.h @@@ -1,119 -1,0 +1,118 @@@ +// 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. + +#ifndef EXTERN +#define EXTERN extern +#endif + +#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__) + +#define TEXTFLAG reg + +EXTERN int32 dynloc; +EXTERN uchar reg[NREG+NFREG]; +EXTERN int32 pcloc; // instruction counter +EXTERN Strlit emptystring; +EXTERN Prog zprog; +EXTERN Node* newproc; +EXTERN Node* deferproc; +EXTERN Node* deferreturn; +EXTERN Node* panicindex; +EXTERN Node* panicslice; +EXTERN Node* panicdiv; +EXTERN Node* throwreturn; +extern vlong unmappedzero; + +/* + * ggen.c + */ +void compile(Node*); +void gen(Node*); +Node* lookdot(Node*, Node*, int); +void cgen_as(Node*, Node*); +void cgen_callmeth(Node*, int); +void cgen_callinter(Node*, Node*, int); +void cgen_proc(Node*, int); +void cgen_callret(Node*, Node*); +void cgen_div(int, Node*, Node*, Node*); +void cgen_hmul(Node*, Node*, Node*); +void cgen_shift(int, int, Node*, Node*, Node*); +void cgen_dcl(Node*); +int needconvert(Type*, Type*); +void genconv(Type*, Type*); +void allocparams(void); +void checklabels(void); +void ginscall(Node*, int); +int gen_as_init(Node*); - void clearslim(Node*); + +/* + * cgen.c + */ +void agen(Node*, Node*); +void agenr(Node*, Node*, Node*); +void cgenr(Node*, Node*, Node*); +void igen(Node*, Node*, Node*); +vlong fieldoffset(Type*, Node*); +void sgen(Node*, Node*, int64); +void gmove(Node*, Node*); +Prog* gins(int, Node*, Node*); +void naddr(Node*, Addr*, int); +void cgen_aret(Node*, Node*); +int componentgen(Node*, Node*); + +/* + * gsubr.c + */ +void clearp(Prog*); +Prog* gbranch(int, Type*, int); +Prog* prog(int); +void gconv(int, int); +int conv2pt(Type*); +vlong convvtox(vlong, int); +void fnparam(Type*, int, int); +Prog* gop(int, Node*, Node*, Node*); +int optoas(int, Type*); +void ginit(void); +void gclean(void); +void regalloc(Node*, Type*, Node*); +void regfree(Node*); +Node* nodarg(Type*, int); +void nodreg(Node*, Type*, int); +void nodindreg(Node*, Type*, int); +void ginscon(int, vlong, Node*); +void ginscon2(int, Node*, vlong); +void buildtxt(void); +Plist* newplist(void); +int isfat(Type*); +void sudoclean(void); +int sudoaddable(int, Node*, Addr*); +void afunclit(Addr*, Node*); +void nodfconst(Node*, Type*, Mpflt*); +void gtrack(Sym*); +void gargsize(vlong); +void fixlargeoffset(Node *n); + +/* + * cplx.c + */ +int complexop(Node*, Node*); +void complexmove(Node*, Node*); +void complexgen(Node*, Node*); + +/* + * gobj.c + */ +void datastring(char*, int, Addr*); +void datagostring(Strlit*, Addr*); + +/* + * list.c + */ +void listinit(void); + +void zaddr(Biobuf*, Addr*, int, int); diff --cc src/cmd/9g/gsubr.c index dfdff05879,0000000000..b194cfd9ee mode 100644,000000..100644 --- a/src/cmd/9g/gsubr.c +++ b/src/cmd/9g/gsubr.c @@@ -1,1720 -1,0 +1,1715 @@@ +// 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 +#include +#include "gg.h" +#include "../../pkg/runtime/funcdata.h" + +// TODO(rsc): Can make this bigger if we move +// the text segment up higher in 6l for all GOOS. +// At the same time, can raise StackBig in ../../pkg/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 = D_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 = D_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 != D_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 != D_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 = D_CONST; + p->to.offset = nam->type->width; + if(nam->readonly) + p->reg = RODATA; + if(nam->type != T && !haspointers(nam->type)) + p->reg |= NOPTR; +} + +void +gtrack(Sym *s) +{ + Prog *p; + + p = gins(AUSEFIELD, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = linksym(s); +} + +void +gargsize(vlong size) +{ + Node n1, n2; + + nodconst(&n1, types[TINT32], PCDATA_ArgSize); + nodconst(&n2, types[TINT32], size); + gins(APCDATA, &n1, &n2); +} + +void +ggloblsym(Sym *s, int32 width, int8 flags) +{ + Prog *p; + + p = gins(AGLOBL, N, N); + p->from.type = D_OREG; + p->from.name = D_EXTERN; + p->from.sym = linksym(s); + p->to.type = D_CONST; + p->to.name = D_NONE; + p->to.offset = width; + p->reg = 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 == D_CONST && a->name == D_EXTERN) { + a->type = D_OREG; + a->sym = linksym(n->sym); + } +} + +static int resvd[] = +{ + REGZERO, + REGSP, // reserved for SP, XXX: not reserved in 9c. + 30, // for g + REGTMP, // REGTMP + FREGCVI+NREG, + FREGZERO+NREG, + FREGHALF+NREG, + FREGONE+NREG, + FREGTWO+NREG, +}; + +void +ginit(void) +{ + int i; + + for(i=0; ietype]; + + if(debug['r']) { + fixfree = 0; + fltfree = 0; + for(i = D_R0; i < D_F0+NREG; i++) + if(reg[i] == 0) { + if(i < D_F0) + fixfree++; + else + fltfree++; + } + print("regalloc fix %d flt %d free\n", fixfree, fltfree); + } + + switch(et) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TPTR32: + case TPTR64: + case TBOOL: + if(o != N && o->op == OREGISTER) { + i = o->val.u.reg; + if(i >= D_R0+REGMIN && i <= D_R0+REGMAX) + goto out; + } + for(i=D_R0+REGMIN; i<=D_R0+REGMAX; i++) + if(reg[i] == 0) { + regpc[i] = (uintptr)getcallerpc(&n); + goto out; + } + flusherrors(); + for(i=D_R0; iop == OREGISTER) { + i = o->val.u.reg; + if(i >= D_F0+FREGMIN && i <= D_F0+FREGMAX) + goto out; + } + for(i=D_F0+FREGMIN; i<=D_F0+FREGMAX; i++) + if(reg[i] == 0) { + regpc[i] = (uintptr)getcallerpc(&n); + goto out; + } + flusherrors(); + for(i=D_F0; iop == ONAME) + return; + if(n->op != OREGISTER && n->op != OINDREG) + fatal("regfree: not a register"); + i = n->val.u.reg; + if(i == D_R0 + REGSP) + return; + if(i < 0 || i >= nelem(reg)) + fatal("regfree: reg out of range"); + if(reg[i] <= 0) + fatal("regfree: reg not allocated"); + reg[i]--; + if(reg[i] == 0) + regpc[i] = 0; +} + +/* + * 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 = D_R0+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 = D_R0 + REGSP; + n->xoffset += types[tptr]->width; + break; + } + n->typecheck = 1; + return n; +} + +/* + * generate + * as $c, n + */ +void +ginscon(int as, vlong c, Node *n2) +{ + Node n1, ntmp; + + nodconst(&n1, types[TINT64], c); + + if(as != AMOVD && (c < -BIG || c > BIG)) { + // cannot have more than 16-bit of immediate in ADD, etc. + // instead, MOV into register first. + regalloc(&ntmp, types[TINT64], N); + gins(AMOVD, &n1, &ntmp); + gins(as, &ntmp, n2); + regfree(&ntmp); + return; + } + gins(as, &n1, n2); +} + +/* + * generate + * as n, $c (CMP/CMPU) + */ +void +ginscon2(int as, Node *n2, vlong c) +{ + Node n1, ntmp; + + nodconst(&n1, types[TINT64], c); + + switch(as) { + default: + fatal("ginscon2"); + case ACMP: + if(-BIG <= c && c <= BIG) { + gins(as, n2, &n1); + return; + } + break; + case ACMPU: + if(0 <= c && c <= 2*BIG) { + gins(as, n2, &n1); + return; + } + break; + } + // MOV n1 into register first + regalloc(&ntmp, types[TINT64], N); + gins(AMOVD, &n1, &ntmp); + gins(as, n2, &ntmp); + regfree(&ntmp); +} + +#define CASE(a,b) (((a)<<16)|((b)<<0)) +/*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: - return 1; + case OADDR: - //if(flag_largemodel) - return 1; - break; ++ return 1; + } + return 0; +} + +/* + * set up nodes representing 2^63 + */ +Node bigi; +Node bigf; + +void +bignodes(void) +{ + static int did; + + if(did) + return; + did = 1; + + nodconst(&bigi, types[TUINT64], 1); + mpshiftfix(bigi.val.u.xval, 63); + + bigf = bigi; + bigf.type = types[TFLOAT64]; + bigf.val.ctype = CTFLT; + bigf.val.u.fval = mal(sizeof *bigf.val.u.fval); + mpmovefixflt(bigf.val.u.fval, bigi.val.u.xval); +} + +/* + * generate move: + * t = f + * hard part is conversions. + */ +void +gmove(Node *f, Node *t) +{ + int a, ft, tt; + Type *cvt; + Node r1, r2, r3, con; + Prog *p1, *p2; + + if(debug['M']) + print("gmove %lN -> %lN\n", f, t); + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + cvt = t->type; + + if(iscomplex[ft] || iscomplex[tt]) { + complexmove(f, t); + return; + } + + // cannot have two memory operands + if(ismem(f) && ismem(t)) + goto hard; + + // convert constant to desired type + if(f->op == OLITERAL) { + switch(tt) { + default: + convconst(&con, t->type, &f->val); + break; + + case TINT32: + case TINT16: + case TINT8: + convconst(&con, types[TINT64], &f->val); + regalloc(&r1, con.type, t); + gins(AMOVD, &con, &r1); + gmove(&r1, t); + regfree(&r1); + return; + + case TUINT32: + case TUINT16: + case TUINT8: + convconst(&con, types[TUINT64], &f->val); + regalloc(&r1, con.type, t); + gins(AMOVD, &con, &r1); + gmove(&r1, t); + regfree(&r1); + return; + } + + f = &con; + ft = tt; // so big switch will choose a simple mov + + // constants can't move directly to memory. + if(ismem(t)) { + goto hard; + // float constants come from memory. + //if(isfloat[tt]) + // goto hard; + + // 64-bit immediates are also from memory. + //if(isint[tt]) + // goto hard; + //// 64-bit immediates are really 32-bit sign-extended + //// unless moving into a register. + //if(isint[tt]) { + // if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) + // goto hard; + // if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) + // goto hard; + //} + } + } + + // value -> value copy, only one memory operand. + // figure out the instruction to use. + // break out of switch for one-instruction gins. + // goto rdst for "destination must be register". + // goto hard for "convert to cvt type first". + // otherwise handle and return. + + switch(CASE(ft, tt)) { + default: + fatal("gmove %lT -> %lT", f->type, t->type); + + /* + * integer copy and truncate + */ + case CASE(TINT8, TINT8): // same size + case CASE(TUINT8, TINT8): + case CASE(TINT16, TINT8): // truncate + case CASE(TUINT16, TINT8): + case CASE(TINT32, TINT8): + case CASE(TUINT32, TINT8): + case CASE(TINT64, TINT8): + case CASE(TUINT64, TINT8): + a = AMOVB; + break; + + case CASE(TINT8, TUINT8): // same size + case CASE(TUINT8, TUINT8): + case CASE(TINT16, TUINT8): // truncate + case CASE(TUINT16, TUINT8): + case CASE(TINT32, TUINT8): + case CASE(TUINT32, TUINT8): + case CASE(TINT64, TUINT8): + case CASE(TUINT64, TUINT8): + a = AMOVBZ; + break; + + case CASE(TINT16, TINT16): // same size + case CASE(TUINT16, TINT16): + case CASE(TINT32, TINT16): // truncate + case CASE(TUINT32, TINT16): + case CASE(TINT64, TINT16): + case CASE(TUINT64, TINT16): + a = AMOVH; + break; + + case CASE(TINT16, TUINT16): // same size + case CASE(TUINT16, TUINT16): + case CASE(TINT32, TUINT16): // truncate + case CASE(TUINT32, TUINT16): + case CASE(TINT64, TUINT16): + case CASE(TUINT64, TUINT16): + a = AMOVHZ; + break; + + case CASE(TINT32, TINT32): // same size + case CASE(TUINT32, TINT32): + case CASE(TINT64, TINT32): // truncate + case CASE(TUINT64, TINT32): + a = AMOVW; + break; + + case CASE(TINT32, TUINT32): // same size + case CASE(TUINT32, TUINT32): + case CASE(TINT64, TUINT32): + case CASE(TUINT64, TUINT32): + a = AMOVWZ; + break; + + case CASE(TINT64, TINT64): // same size + case CASE(TINT64, TUINT64): + case CASE(TUINT64, TINT64): + case CASE(TUINT64, TUINT64): + a = AMOVD; + break; + + /* + * integer up-conversions + */ + case CASE(TINT8, TINT16): // sign extend int8 + case CASE(TINT8, TUINT16): + case CASE(TINT8, TINT32): + case CASE(TINT8, TUINT32): + case CASE(TINT8, TINT64): + case CASE(TINT8, TUINT64): + a = AMOVB; + goto rdst; + + case CASE(TUINT8, TINT16): // zero extend uint8 + case CASE(TUINT8, TUINT16): + case CASE(TUINT8, TINT32): + case CASE(TUINT8, TUINT32): + case CASE(TUINT8, TINT64): + case CASE(TUINT8, TUINT64): + a = AMOVBZ; + goto rdst; + + case CASE(TINT16, TINT32): // sign extend int16 + case CASE(TINT16, TUINT32): + case CASE(TINT16, TINT64): + case CASE(TINT16, TUINT64): + a = AMOVH; + goto rdst; + + case CASE(TUINT16, TINT32): // zero extend uint16 + case CASE(TUINT16, TUINT32): + case CASE(TUINT16, TINT64): + case CASE(TUINT16, TUINT64): + a = AMOVHZ; + goto rdst; + + case CASE(TINT32, TINT64): // sign extend int32 + case CASE(TINT32, TUINT64): + a = AMOVW; + goto rdst; + + case CASE(TUINT32, TINT64): // zero extend uint32 + case CASE(TUINT32, TUINT64): + a = AMOVWZ; + goto rdst; + + /* + * float to integer + */ + case CASE(TFLOAT32, TINT32): + case CASE(TFLOAT64, TINT32): + case CASE(TFLOAT32, TINT64): + case CASE(TFLOAT64, TINT64): + case CASE(TFLOAT32, TINT16): + case CASE(TFLOAT32, TINT8): + case CASE(TFLOAT32, TUINT16): + case CASE(TFLOAT32, TUINT8): + case CASE(TFLOAT64, TINT16): + case CASE(TFLOAT64, TINT8): + case CASE(TFLOAT64, TUINT16): + case CASE(TFLOAT64, TUINT8): + case CASE(TFLOAT32, TUINT32): + case CASE(TFLOAT64, TUINT32): + case CASE(TFLOAT32, TUINT64): + case CASE(TFLOAT64, TUINT64): + //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); + //return; + // algorithm is: + // if small enough, use native float64 -> int64 conversion. + // otherwise, subtract 2^63, convert, and add it back. + bignodes(); + regalloc(&r1, types[ft], f); + gmove(f, &r1); + if(tt == TUINT64) { + regalloc(&r2, types[TFLOAT64], N); + gmove(&bigf, &r2); + gins(AFCMPU, &r1, &r2); + p1 = gbranch(optoas(OLT, types[TFLOAT64]), T, +1); + gins(AFSUB, &r2, &r1); + patch(p1, pc); + regfree(&r2); + } + regalloc(&r2, types[TFLOAT64], N); + regalloc(&r3, types[TINT64], t); + gins(AFCTIDZ, &r1, &r2); + p1 = gins(AFMOVD, &r2, N); + p1->to.type = D_OREG; + p1->to.reg = REGSP; + p1->to.offset = -8; + p1 = gins(AMOVD, N, &r3); + p1->from.type = D_OREG; + p1->from.reg = REGSP; + p1->from.offset = -8; + regfree(&r2); + regfree(&r1); + if(tt == TUINT64) { + p1 = gbranch(optoas(OLT, types[TFLOAT64]), T, +1); // use CR0 here again + nodreg(&r1, types[TINT64], D_R0+REGTMP); + gins(AMOVD, &bigi, &r1); + gins(AADD, &r1, &r3); + patch(p1, pc); + } + gmove(&r3, t); + regfree(&r3); + return; + + /* + * integer to float + */ + case CASE(TINT32, TFLOAT32): + case CASE(TINT32, TFLOAT64): + case CASE(TINT64, TFLOAT32): + case CASE(TINT64, TFLOAT64): + case CASE(TINT16, TFLOAT32): + case CASE(TINT16, TFLOAT64): + case CASE(TINT8, TFLOAT32): + case CASE(TINT8, TFLOAT64): + case CASE(TUINT16, TFLOAT32): + case CASE(TUINT16, TFLOAT64): + case CASE(TUINT8, TFLOAT32): + case CASE(TUINT8, TFLOAT64): + case CASE(TUINT32, TFLOAT32): + case CASE(TUINT32, TFLOAT64): + case CASE(TUINT64, TFLOAT32): + case CASE(TUINT64, TFLOAT64): + //warn("gmove: convert int to float not implemented: %N -> %N\n", f, t); + //return; + // algorithm is: + // if small enough, use native int64 -> uint64 conversion. + // otherwise, halve (rounding to odd?), convert, and double. + bignodes(); + regalloc(&r1, types[TINT64], N); + gmove(f, &r1); + if(ft == TUINT64) { + nodreg(&r2, types[TUINT64], D_R0+REGTMP); + gmove(&bigi, &r2); + gins(ACMPU, &r1, &r2); + p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1); + p2 = gins(ASRD, N, &r1); + p2->from.type = D_CONST; + p2->from.offset = 1; + patch(p1, pc); + } + regalloc(&r2, types[TFLOAT64], t); + p1 = gins(AMOVD, &r1, N); + p1->to.type = D_OREG; + p1->to.reg = REGSP; + p1->to.offset = -8; + p1 = gins(AFMOVD, N, &r2); + p1->from.type = D_OREG; + p1->from.reg = REGSP; + p1->from.offset = -8; + gins(AFCFID, &r2, &r2); + regfree(&r1); + if(ft == TUINT64) { + p1 = gbranch(optoas(OLT, types[TUINT64]), T, +1); // use CR0 here again + nodreg(&r1, types[TFLOAT64], D_F0+FREGTWO); + gins(AFMUL, &r1, &r2); + patch(p1, pc); + } + gmove(&r2, t); + regfree(&r2); + return; + + /* + * float to float + */ + case CASE(TFLOAT32, TFLOAT32): + a = AFMOVS; + break; + + case CASE(TFLOAT64, TFLOAT64): + a = AFMOVD; + break; + + case CASE(TFLOAT32, TFLOAT64): + a = AFMOVS; + goto rdst; + + case CASE(TFLOAT64, TFLOAT32): + a = AFRSP; + goto rdst; + } + + gins(a, f, t); + return; + +rdst: + // requires register destination + regalloc(&r1, t->type, t); + gins(a, f, &r1); + gmove(&r1, t); + regfree(&r1); + return; + +hard: + // requires register intermediate + regalloc(&r1, cvt, t); + gmove(f, &r1); + gmove(&r1, t); + regfree(&r1); + return; +} + +/* + * generate one instruction: + * as f, t + */ +Prog* +gins(int as, Node *f, Node *t) +{ + //int32 w; + Prog *p; + Addr af, at; + + memset(&af, 0, sizeof af); + memset(&at, 0, sizeof at); + if(f != N) + naddr(f, &af, 1); + if(t != N) + naddr(t, &at, 1); + p = prog(as); + if(f != N) + p->from = af; + if(t != N) + p->to = at; + if(as == ATEXT) + p->reg = 0; + if(debug['g']) + print("%P\n", p); + + // TODO(minux): enable these. + // right now it fails on MOVD $type."".TypeAssertionError(SB) [width=1], R7 [width=8] + /* + w = 0; + switch(as) { + case AMOVB: + case AMOVBU: + case AMOVBZ: + case AMOVBZU: + w = 1; + break; + case AMOVH: + case AMOVHU: + case AMOVHZ: + case AMOVHZU: + w = 2; + break; + case AMOVW: + case AMOVWU: + case AMOVWZ: + case AMOVWZU: + w = 4; + break; + case AMOVD: + case AMOVDU: + w = 8; + break; + } + if(w != 0 && ((f != N && af.width < w) || (t != N && at.width > w))) { + dump("f", f); + dump("t", t); + fatal("bad width: %P (%d, %d)\n", p, af.width, at.width); + } + */ + + return p; +} + +void +fixlargeoffset(Node *n) +{ + Node a; + + if(n == N) + return; + if(n->op != OINDREG) + return; + if(n->val.u.reg == D_R0+REGSP) // stack offset cannot be large + return; + 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; + a = *n; + a.op = OREGISTER; + a.type = types[tptr]; + a.xoffset = 0; + cgen_checknil(&a); + ginscon(optoas(OADD, types[tptr]), n->xoffset, &a); + n->xoffset = 0; + } +} + +/* + * generate code to compute n; + * make a refer to result. + */ +void +naddr(Node *n, Addr *a, int canemitcode) +{ + Sym *s; + + a->type = D_NONE; + a->name = D_NONE; + a->reg = NREG; + 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->width = 0; + a->reg = NREG; + 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 = D_OREG; + switch(n->class) { + default: + fatal("naddr: ONAME class %S %d\n", n->sym, n->class); + case PEXTERN: + a->name = D_EXTERN; + break; + case PAUTO: + a->name = D_AUTO; + break; + case PPARAM: + case PPARAMOUT: + a->name = D_PARAM; + break; + case PFUNC: + a->name = D_EXTERN; + a->type = D_CONST; + 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 = D_FCONST; + a->u.dval = mpgetflt(n->val.u.fval); + break; + case CTINT: + case CTRUNE: + a->sym = nil; + a->type = D_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 = D_CONST; + a->offset = n->val.u.bval; + break; + case CTNIL: + a->sym = nil; + a->type = D_CONST; + a->offset = 0; + break; + } + break; + + case OREGISTER: + if(n->val.u.reg < D_F0) { + a->type = D_REG; + a->reg = n->val.u.reg; + } else { + a->type = D_FREG; + a->reg = n->val.u.reg - D_F0; + } + a->sym = nil; + break; + + case OINDREG: + a->type = D_OREG; + 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 = D_OREG; + a->name = D_PARAM; + a->node = n->left->orig; + break; + + case OCLOSUREVAR: + if(!curfn->needctxt) + fatal("closurevar without needctxt"); + a->type = D_OREG; + 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 == D_CONST && a->offset == 0) + break; // len(nil) + break; + + case OSPTR: + // pointer in a string or slice + naddr(n->left, a, canemitcode); + if(a->type == D_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 = simtype[TINT]; + if(a->type == D_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 = simtype[TINT]; + if(a->type == D_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 D_OREG: + a->type = D_CONST; + break; + + case D_REG: + case D_CONST: + break; + + default: + fatal("naddr: OADDR %d\n", a->type); + } + } +} + +/* + * return Axxx for Oxxx on type t. + */ +int +optoas(int op, Type *t) +{ + int a; + + if(t == T) + fatal("optoas: t is nil"); + + a = AGOK; + switch(CASE(op, simtype[t->etype])) { + default: + fatal("optoas: no entry for op=%O type=%T", op, t); + break; + + case CASE(OEQ, TBOOL): + case CASE(OEQ, TINT8): + case CASE(OEQ, TUINT8): + case CASE(OEQ, TINT16): + case CASE(OEQ, TUINT16): + case CASE(OEQ, TINT32): + case CASE(OEQ, TUINT32): + case CASE(OEQ, TINT64): + case CASE(OEQ, TUINT64): + case CASE(OEQ, TPTR32): + case CASE(OEQ, TPTR64): + case CASE(OEQ, TFLOAT32): + case CASE(OEQ, TFLOAT64): + a = ABEQ; + break; + + case CASE(ONE, TBOOL): + case CASE(ONE, TINT8): + case CASE(ONE, TUINT8): + case CASE(ONE, TINT16): + case CASE(ONE, TUINT16): + case CASE(ONE, TINT32): + case CASE(ONE, TUINT32): + case CASE(ONE, TINT64): + case CASE(ONE, TUINT64): + case CASE(ONE, TPTR32): + case CASE(ONE, TPTR64): + case CASE(ONE, TFLOAT32): + case CASE(ONE, TFLOAT64): + a = ABNE; + break; + + case CASE(OLT, TINT8): // ACMP + case CASE(OLT, TINT16): + case CASE(OLT, TINT32): + case CASE(OLT, TINT64): + case CASE(OLT, TUINT8): // ACMPU + case CASE(OLT, TUINT16): + case CASE(OLT, TUINT32): + case CASE(OLT, TUINT64): + case CASE(OLT, TFLOAT32): // AFCMPU + case CASE(OLT, TFLOAT64): + a = ABLT; + break; + + case CASE(OLE, TINT8): // ACMP + case CASE(OLE, TINT16): + case CASE(OLE, TINT32): + case CASE(OLE, TINT64): + case CASE(OLE, TUINT8): // ACMPU + case CASE(OLE, TUINT16): + case CASE(OLE, TUINT32): + case CASE(OLE, TUINT64): + case CASE(OLE, TFLOAT32): // AFCMPU + case CASE(OLE, TFLOAT64): + a = ABLE; + break; + + case CASE(OGT, TINT8): + case CASE(OGT, TINT16): + case CASE(OGT, TINT32): + case CASE(OGT, TINT64): + case CASE(OGT, TUINT8): + case CASE(OGT, TUINT16): + case CASE(OGT, TUINT32): + case CASE(OGT, TUINT64): + case CASE(OGT, TFLOAT32): + case CASE(OGT, TFLOAT64): + a = ABGT; + break; + + case CASE(OGE, TINT8): + case CASE(OGE, TINT16): + case CASE(OGE, TINT32): + case CASE(OGE, TINT64): + case CASE(OGE, TUINT8): + case CASE(OGE, TUINT16): + case CASE(OGE, TUINT32): + case CASE(OGE, TUINT64): + case CASE(OGE, TFLOAT32): + case CASE(OGE, TFLOAT64): + a = ABGE; + break; + + case CASE(OCMP, TBOOL): + case CASE(OCMP, TINT8): + case CASE(OCMP, TINT16): + case CASE(OCMP, TINT32): + case CASE(OCMP, TPTR32): + case CASE(OCMP, TINT64): + a = ACMP; + break; + + case CASE(OCMP, TUINT8): + case CASE(OCMP, TUINT16): + case CASE(OCMP, TUINT32): + case CASE(OCMP, TUINT64): + case CASE(OCMP, TPTR64): + a = ACMPU; + break; + + case CASE(OCMP, TFLOAT32): + case CASE(OCMP, TFLOAT64): + a = AFCMPU; + break; + + case CASE(OAS, TBOOL): + case CASE(OAS, TINT8): + a = AMOVB; + break; + + case CASE(OAS, TUINT8): + a = AMOVBZ; + break; + + case CASE(OAS, TINT16): + a = AMOVH; + break; + + case CASE(OAS, TUINT16): + a = AMOVHZ; + break; + + case CASE(OAS, TINT32): + a = AMOVW; + break; + + case CASE(OAS, TUINT32): + case CASE(OAS, TPTR32): + a = AMOVWZ; + break; + + case CASE(OAS, TINT64): + case CASE(OAS, TUINT64): + case CASE(OAS, TPTR64): + a = AMOVD; + break; + + case CASE(OAS, TFLOAT32): + a = AFMOVS; + break; + + case CASE(OAS, TFLOAT64): + a = AFMOVD; + break; + + case CASE(OADD, TINT8): + case CASE(OADD, TUINT8): + case CASE(OADD, TINT16): + case CASE(OADD, TUINT16): + case CASE(OADD, TINT32): + case CASE(OADD, TUINT32): + case CASE(OADD, TPTR32): - case CASE(OADDPTR, TPTR32): + case CASE(OADD, TINT64): + case CASE(OADD, TUINT64): + case CASE(OADD, TPTR64): - case CASE(OADDPTR, TPTR64): + a = AADD; + break; + + case CASE(OADD, TFLOAT32): + a = AFADDS; + break; + + case CASE(OADD, TFLOAT64): + a = AFADD; + break; + + case CASE(OSUB, TINT8): + case CASE(OSUB, TUINT8): + case CASE(OSUB, TINT16): + case CASE(OSUB, TUINT16): + case CASE(OSUB, TINT32): + case CASE(OSUB, TUINT32): + case CASE(OSUB, TPTR32): + case CASE(OSUB, TINT64): + case CASE(OSUB, TUINT64): + case CASE(OSUB, TPTR64): + a = ASUB; + break; + + case CASE(OSUB, TFLOAT32): + a = AFSUBS; + break; + + case CASE(OSUB, TFLOAT64): + a = AFSUB; + break; + + case CASE(OMINUS, TINT8): + case CASE(OMINUS, TUINT8): + case CASE(OMINUS, TINT16): + case CASE(OMINUS, TUINT16): + case CASE(OMINUS, TINT32): + case CASE(OMINUS, TUINT32): + case CASE(OMINUS, TPTR32): + case CASE(OMINUS, TINT64): + case CASE(OMINUS, TUINT64): + case CASE(OMINUS, TPTR64): + a = ANEG; + break; + + case CASE(OAND, TINT8): + case CASE(OAND, TUINT8): + case CASE(OAND, TINT16): + case CASE(OAND, TUINT16): + case CASE(OAND, TINT32): + case CASE(OAND, TUINT32): + case CASE(OAND, TPTR32): + case CASE(OAND, TINT64): + case CASE(OAND, TUINT64): + case CASE(OAND, TPTR64): + a = AAND; + break; + + case CASE(OOR, TINT8): + case CASE(OOR, TUINT8): + case CASE(OOR, TINT16): + case CASE(OOR, TUINT16): + case CASE(OOR, TINT32): + case CASE(OOR, TUINT32): + case CASE(OOR, TPTR32): + case CASE(OOR, TINT64): + case CASE(OOR, TUINT64): + case CASE(OOR, TPTR64): + a = AOR; + break; + + case CASE(OXOR, TINT8): + case CASE(OXOR, TUINT8): + case CASE(OXOR, TINT16): + case CASE(OXOR, TUINT16): + case CASE(OXOR, TINT32): + case CASE(OXOR, TUINT32): + case CASE(OXOR, TPTR32): + case CASE(OXOR, TINT64): + case CASE(OXOR, TUINT64): + case CASE(OXOR, TPTR64): + a = AXOR; + break; + + // TODO(minux): handle rotates + //case CASE(OLROT, TINT8): + //case CASE(OLROT, TUINT8): + //case CASE(OLROT, TINT16): + //case CASE(OLROT, TUINT16): + //case CASE(OLROT, TINT32): + //case CASE(OLROT, TUINT32): + //case CASE(OLROT, TPTR32): + //case CASE(OLROT, TINT64): + //case CASE(OLROT, TUINT64): + //case CASE(OLROT, TPTR64): + // a = 0//???; RLDC? + // break; + + case CASE(OLSH, TINT8): + case CASE(OLSH, TUINT8): + case CASE(OLSH, TINT16): + case CASE(OLSH, TUINT16): + case CASE(OLSH, TINT32): + case CASE(OLSH, TUINT32): + case CASE(OLSH, TPTR32): + case CASE(OLSH, TINT64): + case CASE(OLSH, TUINT64): + case CASE(OLSH, TPTR64): + a = ASLD; + break; + + case CASE(ORSH, TUINT8): + case CASE(ORSH, TUINT16): + case CASE(ORSH, TUINT32): + case CASE(ORSH, TPTR32): + case CASE(ORSH, TUINT64): + case CASE(ORSH, TPTR64): + a = ASRD; + break; + + case CASE(ORSH, TINT8): + case CASE(ORSH, TINT16): + case CASE(ORSH, TINT32): + case CASE(ORSH, TINT64): + a = ASRAD; + break; + + // TODO(minux): handle rotates + //case CASE(ORROTC, TINT8): + //case CASE(ORROTC, TUINT8): + //case CASE(ORROTC, TINT16): + //case CASE(ORROTC, TUINT16): + //case CASE(ORROTC, TINT32): + //case CASE(ORROTC, TUINT32): + //case CASE(ORROTC, TINT64): + //case CASE(ORROTC, TUINT64): + // a = 0//??? RLDC?? + // break; + + case CASE(OHMUL, TINT64): + a = AMULHD; + break; + case CASE(OHMUL, TUINT64): + case CASE(OHMUL, TPTR64): + a = AMULHDU; + break; + + case CASE(OMUL, TINT8): + case CASE(OMUL, TINT16): + case CASE(OMUL, TINT32): + case CASE(OMUL, TINT64): + a = AMULLD; + break; + + case CASE(OMUL, TUINT8): + case CASE(OMUL, TUINT16): + case CASE(OMUL, TUINT32): + case CASE(OMUL, TPTR32): + // don't use word multiply, the high 32-bit are undefined. + // fallthrough + case CASE(OMUL, TUINT64): + case CASE(OMUL, TPTR64): + a = AMULLD; // for 64-bit multiplies, signedness doesn't matter. + break; + + case CASE(OMUL, TFLOAT32): + a = AFMULS; + break; + + case CASE(OMUL, TFLOAT64): + a = AFMUL; + break; + + case CASE(ODIV, TINT8): + case CASE(ODIV, TINT16): + case CASE(ODIV, TINT32): + case CASE(ODIV, TINT64): + a = ADIVD; + break; + + case CASE(ODIV, TUINT8): + case CASE(ODIV, TUINT16): + case CASE(ODIV, TUINT32): + case CASE(ODIV, TPTR32): + case CASE(ODIV, TUINT64): + case CASE(ODIV, TPTR64): + a = ADIVDU; + break; + + case CASE(ODIV, TFLOAT32): + a = AFDIVS; + break; + + case CASE(ODIV, TFLOAT64): + a = AFDIV; + break; + + } + return a; +} + +enum +{ + ODynam = 1<<0, + OAddable = 1<<1, +}; + +int +xgen(Node *n, Node *a, int o) +{ + // TODO(minux) + USED(n); USED(a); USED(o); + return -1; +} + +void +sudoclean(void) +{ + return; +} + +/* + * generate code to compute address of n, + * a reference to a (perhaps nested) field inside + * an array or struct. + * return 0 on failure, 1 on success. + * on success, leaves usable address in a. + * + * caller is responsible for calling sudoclean + * after successful sudoaddable, + * to release the register used for a. + */ +int +sudoaddable(int as, Node *n, Addr *a) +{ + // TODO(minux) + USED(as); USED(n); USED(a); + return 0; +} diff --cc src/cmd/9l/asm.c index 5aeea1b6b0,0000000000..b8ca777c35 mode 100644,000000..100644 --- a/src/cmd/9l/asm.c +++ b/src/cmd/9l/asm.c @@@ -1,338 -1,0 +1,338 @@@ +// Inferno utils/5l/asm.c +// http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.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. + +// Writing object files. + +#include "l.h" +#include "../ld/lib.h" +#include "../ld/elf.h" +#include "../ld/dwarf.h" + + +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) +{ + char *p; + LSym *s; + + if(*name == '\0') + return 0; + + /* reuse hash code in symbol table */ + p = smprint(".dynlib.%s", name); + s = linklookup(ctxt, p, 0); + free(p); + if(s->type == 0) { + s->type = 100; // avoid SDATA, etc. + return 1; + } + return 0; +} + +int nelfsym = 1; + +// b is the addresses, a is the I-form branch instruction template, peform +// addition so that the instruction jumps to address (offset) b. +static int32 +braddoff(int32 a, int32 b) +{ + return (((uint32)a) & 0xfc000003U) | (0x03fffffcU & (uint32)((a & 0x3fffffcU) + b)); +} + +void +adddynrela(LSym *rel, LSym *s, Reloc *r) +{ + // TODO(minux) + USED(rel); USED(s); USED(r); +} + +void +adddynrel(LSym *s, Reloc *r) +{ + LSym *targ; + + // TODO(minux) + + targ = r->sym; + ctxt->cursym = s; + diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); +} + +int +elfreloc1(Reloc *r, vlong sectoff) +{ + USED(r); USED(sectoff); + // TODO(minux) + return -1; +} + +void +elfsetupplt(void) +{ + // TODO(minux) + return; +} + +int +machoreloc1(Reloc *r, vlong sectoff) +{ + USED(r); + USED(sectoff); + + return -1; +} + + +int +archreloc(Reloc *r, LSym *s, vlong *val) +{ + uint32 o1, o2; + int32 t; + + if(linkmode == LinkExternal) { + // TODO(minux): translate R_ADDRPOWER and R_CALLPOWER into standard ELF relocations. + // R_ADDRPOWER corresponds to R_PPC_ADDR16_HA and R_PPC_ADDR16_LO. + // R_CALLPOWER corresponds to R_PPC_REL24. + return -1; + } + switch(r->type) { + case R_CONST: + *val = r->add; + return 0; + case R_GOTOFF: + *val = symaddr(r->sym) + r->add - symaddr(linklookup(ctxt, ".got", 0)); + return 0; + case R_ADDRPOWER: + // r->add is two power64 instructions holding an immediate 32-bit constant. + // We want to add r->sym's address to that constant. + // The encoding of the immediate x<<16 + y, + // where x is the low 16 bits of the first instruction and y is the low 16 + // bits of the second. Both x and y are signed (int16, not uint16). + o1 = r->add >> 32; + o2 = r->add; + t = symaddr(r->sym); + if(t < 0) { + ctxt->diag("relocation for %s is too big (>=2G): %lld", s->name, symaddr(r->sym)); + } + t += ((o1 & 0xffff) << 16) + ((int32)o2 << 16 >> 16); + if(t & 0x8000) + t += 0x10000; + o1 = (o1 & 0xffff0000) | ((t >> 16) & 0xffff); + o2 = (o2 & 0xffff0000) | (t & 0xffff); + // when laid out, the instruction order must always be o1, o2. + if(ctxt->arch->endian == BigEndian) + *val = ((vlong)o1 << 32) | o2; + else + *val = ((vlong)o2 << 32) | o1; + return 0; + case R_CALLPOWER: + *val = braddoff((uint32)r->add, (int32)(symaddr(r->sym) - (s->value + r->off))); + return 0; + } + return -1; +} + +void +adddynsym(Link *ctxt, LSym *s) +{ + USED(ctxt); USED(s); + // TODO(minux) + return; +} + +void +adddynlib(char *lib) +{ + LSym *s; + + if(!needlib(lib)) + return; + + if(iself) { + s = linklookup(ctxt, ".dynstr", 0); + if(s->size == 0) + addstring(s, ""); + elfwritedynent(linklookup(ctxt, ".dynamic", 0), DT_NEEDED, addstring(s, lib)); + } else { + diag("adddynlib: unsupported binary format"); + } +} + +void +asmb(void) +{ + uint32 symo; + Section *sect; + LSym *sym; + int i; + + if(debug['v']) + Bprint(&bso, "%5.2f asmb\n", cputime()); + Bflush(&bso); + + if(iself) + asmbelfsetup(); + + sect = segtext.sect; + cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); + codeblk(sect->vaddr, sect->len); + for(sect = sect->next; sect != nil; sect = sect->next) { + cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); + datblk(sect->vaddr, sect->len); + } + + if(segrodata.filelen > 0) { + if(debug['v']) + Bprint(&bso, "%5.2f rodatblk\n", cputime()); + Bflush(&bso); + + cseek(segrodata.fileoff); + datblk(segrodata.vaddr, segrodata.filelen); + } + + if(debug['v']) + Bprint(&bso, "%5.2f datblk\n", cputime()); + Bflush(&bso); + + cseek(segdata.fileoff); + datblk(segdata.vaddr, segdata.filelen); + + /* output symbol table */ + symsize = 0; + lcsize = 0; + symo = 0; + if(!debug['s']) { + // TODO: rationalize + if(debug['v']) + Bprint(&bso, "%5.2f sym\n", cputime()); + Bflush(&bso); + switch(HEADTYPE) { + default: + if(iself) + goto ElfSym; + case Hplan9: - symo = HEADR+segtext.len+segdata.filelen; ++ symo = segdata.fileoff+segdata.filelen; + break; + ElfSym: - symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; ++ symo = segdata.fileoff+segdata.filelen; + symo = rnd(symo, INITRND); + break; + } + cseek(symo); + switch(HEADTYPE) { + default: + if(iself) { + if(debug['v']) + Bprint(&bso, "%5.2f elfsym\n", cputime()); + asmelfsym(); + cflush(); + cwrite(elfstrdat, elfstrsize); + + if(debug['v']) + Bprint(&bso, "%5.2f dwarf\n", cputime()); + dwarfemitdebugsections(); + + if(linkmode == LinkExternal) + elfemitreloc(); + } + break; + case Hplan9: + asmplan9sym(); + cflush(); + + sym = linklookup(ctxt, "pclntab", 0); + if(sym != nil) { + lcsize = sym->np; + for(i=0; i < lcsize; i++) + cput(sym->p[i]); + + cflush(); + } + break; + } + } + + ctxt->cursym = nil; + if(debug['v']) + Bprint(&bso, "%5.2f header\n", cputime()); + Bflush(&bso); + cseek(0L); + 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); + break; + case Hlinux: + case Hfreebsd: + case Hnetbsd: + case Hopenbsd: + case Hnacl: + asmbelf(symo); + break; + } + cflush(); + if(debug['c']){ + print("textsize=%ulld\n", segtext.filelen); + print("datsize=%ulld\n", segdata.filelen); + print("bsssize=%ulld\n", segdata.len - segdata.filelen); + print("symsize=%d\n", symsize); + print("lcsize=%d\n", lcsize); + print("total=%lld\n", 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 --cc src/cmd/cc/cc.y index 11ee444b7d,11ee444b7d..8d7cb1472c --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@@ -1043,6 -1043,6 +1043,7 @@@ complex } | LSTRUCT sbody { ++ diag(Z, "struct must have tag"); taggen++; sprint(symb, "_%d_", taggen); $$ = dotag(lookup(), TSTRUCT, autobn); diff --cc src/pkg/debug/elf/file.go index 5e1c47a04c,c908e7a889..49060b7356 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@@ -529,9 -529,9 +529,12 @@@ func (f *File) applyRelocations(dst []b if f.Class == ELFCLASS32 && f.Machine == EM_386 { return f.applyRelocations386(dst, rels) } + if f.Class == ELFCLASS64 && f.Machine == EM_AARCH64 { + return f.applyRelocationsARM64(dst, rels) + } + if f.Class == ELFCLASS64 && f.Machine == EM_PPC64 { + return f.applyRelocationsPPC64(dst, rels) + } return errors.New("not implemented") } @@@ -618,51 -618,51 +621,96 @@@ func (f *File) applyRelocations386(dst return nil } + func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_AARCH64(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if SymType(sym.Info&0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue + } + + switch t { + case R_AARCH64_ABS64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)) + case R_AARCH64_ABS32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)) + } + } + + return nil + } + +func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error { + // 24 is the size of Rela64. + if len(rels)%24 != 0 { + return errors.New("length of relocation section is not a multiple of 24") + } + + symbols, _, err := f.getSymbols(SHT_SYMTAB) + if err != nil { + return err + } + + b := bytes.NewReader(rels) + var rela Rela64 + + for b.Len() > 0 { + binary.Read(b, f.ByteOrder, &rela) + symNo := rela.Info >> 32 + t := R_PPC64(rela.Info & 0xffff) + + if symNo == 0 || symNo > uint64(len(symbols)) { + continue + } + sym := &symbols[symNo-1] + if SymType(sym.Info&0xf) != STT_SECTION { + // We don't handle non-section relocations for now. + continue + } + + switch t { + case R_PPC64_ADDR64: + if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend)) + case R_PPC64_ADDR32: + if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { + continue + } + f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend)) + } + } + + return nil +} + func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the required ones, and the debug/dwarf package @@@ -685,7 -685,7 +733,7 @@@ // If there's a relocation table for .debug_info, we have to process it // now otherwise the data in .debug_info is invalid for x86-64 objects. rela := f.Section(".rela.debug_info") - if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_PPC64) { - if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64) { ++ if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64 || f.Machine == EM_PPC64) { data, err := rela.Data() if err != nil { return nil, err diff --cc src/pkg/debug/elf/file_test.go index db9a7476c3,5e5ba52f39..d57aaab0ec --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@@ -260,12 -260,12 +260,18 @@@ var relocationTests = []relocationTest {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, }, }, + { + "testdata/go-relocation-test-gcc482-aarch64.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -g -fstack-protector"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: int64(0x24)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, + { + "testdata/go-relocation-test-gcc482-ppc64le.obj", + []relocationTestEntry{ + {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{dwarf.Field{Attr: dwarf.AttrProducer, Val: "GNU C 4.8.2 -Asystem=linux -Asystem=unix -Asystem=posix -msecure-plt -mtune=power8 -mcpu=power7 -gdwarf-2 -fstack-protector"}, dwarf.Field{Attr: dwarf.AttrLanguage, Val: int64(1)}, dwarf.Field{Attr: dwarf.AttrName, Val: "go-relocation-test-gcc482-ppc64le.c"}, dwarf.Field{Attr: dwarf.AttrCompDir, Val: "/tmp"}, dwarf.Field{Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, dwarf.Field{Attr: dwarf.AttrHighpc, Val: uint64(0x24)}, dwarf.Field{Attr: dwarf.AttrStmtList, Val: int64(0)}}}}, + }, + }, { "testdata/go-relocation-test-clang-x86.obj", []relocationTestEntry{ diff --cc src/pkg/math/abs_power64x.s index 8fd8020f98,0000000000..3ba8201b3b mode 100644,000000..100644 --- a/src/pkg/math/abs_power64x.s +++ b/src/pkg/math/abs_power64x.s @@@ -1,14 -1,0 +1,14 @@@ +// 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. + +// +build power64 power64le + - #include "../../cmd/ld/textflag.h" ++#include "textflag.h" + +TEXT ·Abs(SB),NOSPLIT,$0-16 + MOVD x+0(FP), R3 + MOVD $((1<<63)-1), R4 + AND R4, R3 + MOVD R3, ret+8(FP) + RETURN diff --cc src/pkg/math/big/arith_power64x.s index 502d40cb64,0000000000..c33a9209f1 mode 100644,000000..100644 --- a/src/pkg/math/big/arith_power64x.s +++ b/src/pkg/math/big/arith_power64x.s @@@ -1,46 -1,0 +1,46 @@@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build power64 power64le + - #include "../../../cmd/ld/textflag.h" ++#include "textflag.h" + +// This file provides fast assembly versions for the elementary +// arithmetic operations on vectors implemented in arith.go. + +TEXT ·mulWW(SB),NOSPLIT,$0 + BR ·mulWW_g(SB) + +TEXT ·divWW(SB),NOSPLIT,$0 + BR ·divWW_g(SB) + +TEXT ·addVV(SB),NOSPLIT,$0 + BR ·addVV_g(SB) + +TEXT ·subVV(SB),NOSPLIT,$0 + BR ·subVV_g(SB) + +TEXT ·addVW(SB),NOSPLIT,$0 + BR ·addVW_g(SB) + +TEXT ·subVW(SB),NOSPLIT,$0 + BR ·subVW_g(SB) + +TEXT ·shlVU(SB),NOSPLIT,$0 + BR ·shlVU_g(SB) + +TEXT ·shrVU(SB),NOSPLIT,$0 + BR ·shrVU_g(SB) + +TEXT ·mulAddVWW(SB),NOSPLIT,$0 + BR ·mulAddVWW_g(SB) + +TEXT ·addMulVVW(SB),NOSPLIT,$0 + BR ·addMulVVW_g(SB) + +TEXT ·divWVW(SB),NOSPLIT,$0 + BR ·divWVW_g(SB) + +TEXT ·bitLen(SB),NOSPLIT,$0 + BR ·bitLen_g(SB) diff --cc src/pkg/os/signal/sig.s index c324fcda4e,d54c284b5e..1826c37fbd --- a/src/pkg/os/signal/sig.s +++ b/src/pkg/os/signal/sig.s @@@ -4,9 -4,9 +4,9 @@@ // Assembly to get into package runtime without using exported symbols. -// +build amd64 amd64p32 arm 386 +// +build amd64 amd64p32 arm 386 power64 power64le - #include "../../../cmd/ld/textflag.h" + #include "textflag.h" #ifdef GOARCH_arm #define JMP B diff --cc src/pkg/reflect/asm_power64x.s index 78839075c4,0000000000..e430cdf04c mode 100644,000000..100644 --- a/src/pkg/reflect/asm_power64x.s +++ b/src/pkg/reflect/asm_power64x.s @@@ -1,50 -1,0 +1,29 @@@ +// 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 power64 power64le + - #include "../../cmd/ld/textflag.h" ++#include "textflag.h" + +// makeFuncStub is the code half of the function returned by MakeFunc. +// See the comment on the declaration of makeFuncStub in makefunc.go +// for more details. +// No argsize here, gc generates argsize info at call site. +TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16 + MOVD R11, 8(R1) + MOVD $argframe+0(FP), R3 + MOVD R3, 16(R1) + BL ·callReflect(SB) + RETURN + +// methodValueCall is the code half of the function returned by makeMethodValue. +// See the comment on the declaration of methodValueCall in makefunc.go +// for more details. +// No argsize here, gc generates argsize info at call site. +TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16 + MOVD R11, 8(R1) + MOVD $argframe+0(FP), R3 + MOVD R3, 16(R1) + BL ·callMethod(SB) + RETURN - - // Stubs to give reflect package access to runtime services - // TODO: should probably be done another way. - TEXT ·makemap(SB),NOSPLIT,$0-0 - BR runtime·reflect_makemap(SB) - TEXT ·mapaccess(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapaccess(SB) - TEXT ·mapassign(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapassign(SB) - TEXT ·mapdelete(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapdelete(SB) - TEXT ·mapiterinit(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapiterinit(SB) - TEXT ·mapiterkey(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapiterkey(SB) - TEXT ·mapiternext(SB),NOSPLIT,$0-0 - BR runtime·reflect_mapiternext(SB) - TEXT ·maplen(SB),NOSPLIT,$0-0 - BR runtime·reflect_maplen(SB) - TEXT ·ismapkey(SB),NOSPLIT,$0-0 - BR runtime·reflect_ismapkey(SB) diff --cc src/pkg/runtime/defs_linux_power64.h index 41db45ca14,0000000000..64f145672c mode 100644,000000..100644 --- a/src/pkg/runtime/defs_linux_power64.h +++ b/src/pkg/runtime/defs_linux_power64.h @@@ -1,211 -1,0 +1,211 @@@ +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_linux.go defs3_linux.go + + +enum { + EINTR = 0x4, + EAGAIN = 0xb, + ENOMEM = 0xc, + + PROT_NONE = 0x0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + + MAP_ANON = 0x20, + MAP_PRIVATE = 0x2, + MAP_FIXED = 0x10, + + MADV_DONTNEED = 0x4, + + SA_RESTART = 0x10000000, + SA_ONSTACK = 0x8000000, + SA_SIGINFO = 0x4, + + SIGHUP = 0x1, + SIGINT = 0x2, + SIGQUIT = 0x3, + SIGILL = 0x4, + SIGTRAP = 0x5, + SIGABRT = 0x6, + SIGBUS = 0x7, + SIGFPE = 0x8, + SIGKILL = 0x9, + SIGUSR1 = 0xa, + SIGSEGV = 0xb, + SIGUSR2 = 0xc, + SIGPIPE = 0xd, + SIGALRM = 0xe, + SIGSTKFLT = 0x10, + SIGCHLD = 0x11, + SIGCONT = 0x12, + SIGSTOP = 0x13, + SIGTSTP = 0x14, + SIGTTIN = 0x15, + SIGTTOU = 0x16, + SIGURG = 0x17, + SIGXCPU = 0x18, + SIGXFSZ = 0x19, + SIGVTALRM = 0x1a, + SIGPROF = 0x1b, + SIGWINCH = 0x1c, + SIGIO = 0x1d, + SIGPWR = 0x1e, + SIGSYS = 0x1f, + + FPE_INTDIV = 0x1, + FPE_INTOVF = 0x2, + FPE_FLTDIV = 0x3, + FPE_FLTOVF = 0x4, + FPE_FLTUND = 0x5, + FPE_FLTRES = 0x6, + FPE_FLTINV = 0x7, + FPE_FLTSUB = 0x8, + + BUS_ADRALN = 0x1, + BUS_ADRERR = 0x2, + BUS_OBJERR = 0x3, + + SEGV_MAPERR = 0x1, + SEGV_ACCERR = 0x2, + + ITIMER_REAL = 0x0, + ITIMER_VIRTUAL = 0x1, + ITIMER_PROF = 0x2, + + EPOLLIN = 0x1, + EPOLLOUT = 0x4, + EPOLLERR = 0x8, + EPOLLHUP = 0x10, + EPOLLRDHUP = 0x2000, + EPOLLET = -0x80000000, + EPOLL_CLOEXEC = 0x80000, + EPOLL_CTL_ADD = 0x1, + EPOLL_CTL_DEL = 0x2, + EPOLL_CTL_MOD = 0x3, +}; + +typedef struct Sigset Sigset; +typedef struct Timespec Timespec; +typedef struct Timeval Timeval; +typedef struct Sigaction Sigaction; +typedef struct Siginfo Siginfo; +typedef struct Itimerval Itimerval; +typedef struct EpollEvent EpollEvent; +typedef uint64 Usigset; + +#pragma pack on + +//struct Sigset { +// uint64 sig[1]; +//}; +//typedef uint64 Sigset; + +struct Timespec { + int64 tv_sec; + int64 tv_nsec; +}; +struct Timeval { + int64 tv_sec; + int64 tv_usec; +}; +struct Sigaction { + void *sa_handler; + uint64 sa_flags; + void *sa_restorer; + Usigset sa_mask; +}; +struct Siginfo { + int32 si_signo; + int32 si_errno; + int32 si_code; + byte Pad_cgo_0[4]; + byte _sifields[112]; +}; +struct Itimerval { + Timeval it_interval; + Timeval it_value; +}; +struct EpollEvent { + uint32 events; + byte Pad_cgo_0[4]; + uint64 data; +}; + + +#pragma pack off +// Created by cgo -cdefs - DO NOT EDIT +// cgo -cdefs defs_linux.go defs3_linux.go + + +enum { + O_RDONLY = 0x0, + O_CLOEXEC = 0x80000, + SA_RESTORER = 0, +}; + +//typedef struct Usigset Usigset; +typedef struct Ptregs Ptregs; +typedef struct Vreg Vreg; - typedef struct Sigaltstack Sigaltstack; ++typedef struct SigaltstackT SigaltstackT; +typedef struct Sigcontext Sigcontext; +typedef struct Ucontext Ucontext; + +#pragma pack on + +//struct Usigset { +// uint64 sig[1]; +//}; +//typedef Sigset Usigset; + +struct Ptregs { + uint64 gpr[32]; + uint64 nip; + uint64 msr; + uint64 orig_gpr3; + uint64 ctr; + uint64 link; + uint64 xer; + uint64 ccr; + uint64 softe; + uint64 trap; + uint64 dar; + uint64 dsisr; + uint64 result; +}; +typedef uint64 Gregset[48]; +typedef float64 FPregset[33]; +struct Vreg { + uint32 u[4]; +}; + - struct Sigaltstack { ++struct SigaltstackT { + byte *ss_sp; + int32 ss_flags; + byte Pad_cgo_0[4]; + uint64 ss_size; +}; + +struct Sigcontext { + uint64 _unused[4]; + int32 signal; + int32 _pad0; + uint64 handler; + uint64 oldmask; + Ptregs *regs; + uint64 gp_regs[48]; + float64 fp_regs[33]; + Vreg *v_regs; + int64 vmx_reserve[101]; +}; +struct Ucontext { + uint64 uc_flags; + Ucontext *uc_link; - Sigaltstack uc_stack; ++ SigaltstackT uc_stack; + Usigset uc_sigmask; + Usigset __unused[15]; + Sigcontext uc_mcontext; +}; + + +#pragma pack off diff --cc src/pkg/runtime/gcinfo_test.go index 5e5b7ec375,88f6703f97..7d432983b1 --- a/src/pkg/runtime/gcinfo_test.go +++ b/src/pkg/runtime/gcinfo_test.go @@@ -117,17 -147,12 +147,18 @@@ func infoBigStruct() []byte } case "amd64p32": return []byte{ - BitsPointer, BitsScalar, BitsScalar, BitsScalar, - BitsScalar, BitsScalar, BitsMultiWord, BitsSlice, - BitsScalar, BitsScalar, BitsScalar, BitsScalar, - BitsScalar, BitsScalar, BitsMultiWord, BitsString, + BitsPointer, // q *int + BitsScalar, BitsScalar, BitsScalar, BitsScalar, BitsScalar, // w byte; e [17]byte + BitsPointer, BitsDead, BitsDead, // r []byte + BitsScalar, BitsScalar, BitsDead, BitsScalar, BitsScalar, // t int; y uint16; u uint64 + BitsPointer, BitsDead, // i string } + case "power64", "power64le": + return []byte{ + BitsPointer, BitsScalar, BitsScalar, BitsScalar, + BitsMultiWord, BitsSlice, BitsScalar, BitsScalar, + BitsScalar, BitsScalar, BitsMultiWord, BitsString, + } default: panic("unknown arch") } diff --cc src/pkg/runtime/heapdump.c index f29cf0108f,d07fdb29bc..5696470f29 --- a/src/pkg/runtime/heapdump.c +++ b/src/pkg/runtime/heapdump.c @@@ -261,17 -259,9 +259,9 @@@ dumpbv(BitVector *bv, uintptr offset dumpint(offset + i / BitsPerPointer * PtrSize); break; case BitsMultiWord: - switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) { + switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) { - case BitsString: - dumpint(FieldKindString); - dumpint(offset + i / BitsPerPointer * PtrSize); - i += BitsPerPointer; - break; - case BitsSlice: - dumpint(FieldKindSlice); - dumpint(offset + i / BitsPerPointer * PtrSize); - i += 2 * BitsPerPointer; - break; + default: + runtime·throw("unexpected garbage collection bits"); case BitsIface: dumpint(FieldKindIface); dumpint(offset + i / BitsPerPointer * PtrSize); @@@ -795,10 -803,11 +803,11 @@@ dumpbvtypes(BitVector *bv, byte *base uintptr i; for(i = 0; i < bv->n; i += BitsPerPointer) { - if((bv->data[i/32] >> i%32 & 3) != BitsMultiWord) + if((bv->bytedata[i/8] >> i%8 & 3) != BitsMultiWord) continue; - switch(bv->data[(i+BitsPerPointer)/32] >> (i+BitsPerPointer)%32 & 3) { + switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) { - case BitsString: + default: + runtime·throw("unexpected garbage collection bits"); case BitsIface: i += BitsPerPointer; break; @@@ -840,8 -847,8 +847,8 @@@ makeheapobjbv(byte *p, uintptr size if(!mw && bits == BitsDead) break; // end of heap object mw = !mw && bits == BitsMultiWord; - tmpbuf[i*BitsPerPointer/8] &= ~(3<<((i*BitsPerPointer)%8)); + tmpbuf[i*BitsPerPointer/8] &= ~(BitsMask<<((i*BitsPerPointer)%8)); tmpbuf[i*BitsPerPointer/8] |= bits<<((i*BitsPerPointer)%8); } - return (BitVector){i*BitsPerPointer, (uint32*)tmpbuf}; + return (BitVector){i*BitsPerPointer, (byte*)tmpbuf}; } diff --cc src/pkg/runtime/malloc.c index 1e133168b1,752ff60f37..91224a38f3 --- a/src/pkg/runtime/malloc.c +++ b/src/pkg/runtime/malloc.c @@@ -21,7 -21,10 +21,10 @@@ MHeap runtime·mheap #pragma dataflag NOPTR MStats runtime·memstats; + Type* runtime·conservative; + -void runtime·cmallocgc(uintptr size, Type *typ, uint32 flag, void **ret); +void runtime·cmallocgc(uintptr size, Type *typ, intgo flag, void **ret); + void runtime·gc_notype_ptr(Eface*); void* runtime·mallocgc(uintptr size, Type *typ, uint32 flag) diff --cc src/pkg/runtime/malloc.go index df030794b5,883ca0cef7..ea3a467b8f --- a/src/pkg/runtime/malloc.go +++ b/src/pkg/runtime/malloc.go @@@ -4,22 -4,15 +4,13 @@@ package runtime -import ( - "unsafe" -) +import "unsafe" const ( - flagNoScan = 1 << 0 // GC doesn't have to scan object - flagNoProfiling = 1 << 1 // must not profile - flagNoZero = 1 << 3 // don't zero memory - flagNoInvokeGC = 1 << 4 // don't invoke GC - - kindArray = 17 - kindFunc = 19 - kindInterface = 20 - kindPtr = 22 - kindStruct = 25 - kindMask = 1<<6 - 1 - kindGCProg = 1 << 6 - kindNoPointers = 1 << 7 + debugMalloc = false + + flagNoScan = 1 << 0 // GC doesn't have to scan object + flagNoZero = 1 << 1 // don't zero memory maxTinySize = 16 tinySizeClass = 2 @@@ -167,16 -204,96 +202,98 @@@ func gomallocgc(size uintptr, typ *_typ size = uintptr(s.elemsize) } - // TODO: write markallocated in Go - mp.ptrarg[0] = x - mp.scalararg[0] = uint(size) - mp.scalararg[1] = uint(size0) - mp.ptrarg[1] = unsafe.Pointer(typ) - mp.scalararg[2] = uint(flags & flagNoScan) - onM(&markallocated_m) + if flags&flagNoScan != 0 { + // All objects are pre-marked as noscan. + goto marked + } + + // From here till marked label marking the object as allocated + // and storing type info in the GC bitmap. + { + arena_start := uintptr(unsafe.Pointer(mheap_.arena_start)) + off := (uintptr(x) - arena_start) / ptrSize + xbits := (*uint8)(unsafe.Pointer(arena_start - off/wordsPerBitmapByte - 1)) + shift := (off % wordsPerBitmapByte) * gcBits + if debugMalloc && ((*xbits>>shift)&(bitMask|bitPtrMask)) != bitBoundary { + println("runtime: bits =", (*xbits>>shift)&(bitMask|bitPtrMask)) + gothrow("bad bits in markallocated") + } - mp.mallocing = 0 + var ti, te uintptr + var ptrmask *uint8 + if size == ptrSize { + // It's one word and it has pointers, it must be a pointer. + *xbits |= (bitsPointer << 2) << shift + goto marked + } + if typ.kind&kindGCProg != 0 { + nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize + masksize := nptr + if masksize%2 != 0 { + masksize *= 2 // repeated + } + masksize = masksize * pointersPerByte / 8 // 4 bits per word + masksize++ // unroll flag in the beginning + if masksize > maxGCMask && typ.gc[1] != 0 { + // If the mask is too large, unroll the program directly + // into the GC bitmap. It's 7 times slower than copying + // from the pre-unrolled mask, but saves 1/16 of type size + // memory for the mask. + mp := acquirem() + mp.ptrarg[0] = x + mp.ptrarg[1] = unsafe.Pointer(typ) + mp.scalararg[0] = uintptr(size) + mp.scalararg[1] = uintptr(size0) + onM(unrollgcproginplace_m) + releasem(mp) + goto marked + } + ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0]))) - // Check whether the program is already unrolled. - if uintptr(atomicloadp(unsafe.Pointer(ptrmask)))&0xff == 0 { ++ // Check whether the program is already unrolled ++ // by checking if the unroll flag byte is set ++ maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask))) ++ if *(*uint8)(unsafe.Pointer(&maskword)) == 0 { + mp := acquirem() + mp.ptrarg[0] = unsafe.Pointer(typ) + onM(unrollgcprog_m) + releasem(mp) + } + ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte + } else { + ptrmask = (*uint8)(unsafe.Pointer(&typ.gc[0])) // embed mask + } + if size == 2*ptrSize { + *xbits = *ptrmask | bitBoundary + goto marked + } + te = uintptr(typ.size) / ptrSize + // If the type occupies odd number of words, its mask is repeated. + if te%2 == 0 { + te /= 2 + } + // Copy pointer bitmask into the bitmap. + for i := uintptr(0); i < size0; i += 2 * ptrSize { + v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti)) + ti++ + if ti == te { + ti = 0 + } + if i == 0 { + v |= bitBoundary + } + if i+ptrSize == size0 { + v &^= uint8(bitPtrMask << 4) + } + *xbits = v + xbits = (*byte)(add(unsafe.Pointer(xbits), ^uintptr(0))) + } + if size0%(2*ptrSize) == 0 && size0 < size { + // Mark the word after last object's word as bitsDead. + *xbits = bitsDead << 2 + } + } + marked: if raceenabled { racemalloc(x, size) } diff --cc src/pkg/runtime/mgc0.c index b1a8943115,d4c414736b..7ec9640eb6 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@@ -510,11 -438,11 +444,11 @@@ markroot(ParFor *desc, uint32 i // Note: if you add a case here, please also update heapdump.c:dumproots. switch(i) { case RootData: - scanblock(data, edata - data, work.gcdata); - scanblock(runtime·data, runtime·edata - runtime·data, (byte*)runtime·gcdatamask.data); ++ scanblock(runtime·data, runtime·edata - runtime·data, runtime·gcdatamask.bytedata); break; case RootBss: - scanblock(bss, ebss - bss, work.gcbss); - scanblock(runtime·bss, runtime·ebss - runtime·bss, (byte*)runtime·gcbssmask.data); ++ scanblock(runtime·bss, runtime·ebss - runtime·bss, runtime·gcbssmask.bytedata); break; case RootFinalizers: @@@ -733,7 -668,7 +674,7 @@@ scanframe(Stkframe *frame, void *unused } bv = runtime·stackmapdata(stackmap, pcdata); size = (bv.n * PtrSize) / BitsPerPointer; - scanblock(frame->varp - size, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata); - scanblock((byte*)(frame->varp - size), bv.n/BitsPerPointer*PtrSize, (byte*)bv.data); ++ scanblock((byte*)frame->varp - size, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata); } // Scan arguments. @@@ -741,7 -676,7 +682,7 @@@ stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps); if(stackmap != nil) { bv = runtime·stackmapdata(stackmap, pcdata); - scanblock(frame->argp, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata); - scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, (byte*)bv.data); ++ scanblock((byte*)frame->argp, bv.n/BitsPerPointer*PtrSize, (byte*)bv.bytedata); } else { if(Debug > 2) runtime·printf("frame %s conservative args %p+%p\n", runtime·funcname(f), frame->argp, (uintptr)frame->arglen); @@@ -1842,14 -1640,23 +1646,23 @@@ unrollglobgcprog(byte *prog, uintptr si runtime·throw("unrollglobgcprog: program does not end with insEnd"); if(mask[masksize] != 0xa1) runtime·throw("unrollglobgcprog: overflow"); - return mask; - return (BitVector){masksize*8, (uint32*)mask}; ++ return (BitVector){masksize*8, mask}; } - static void - unrollgcproginplace(void *v, uintptr size, uintptr size0, Type *typ) + void + runtime·unrollgcproginplace_m(void) { - uintptr *b, off, shift, pos; - byte *arena_start, *prog; + uintptr size, size0, pos, off; + byte *arena_start, *prog, *bitp, shift; + Type *typ; + void *v; + + v = g->m->ptrarg[0]; + typ = g->m->ptrarg[1]; + size = g->m->scalararg[0]; + size0 = g->m->scalararg[1]; + g->m->ptrarg[0] = nil; + g->m->ptrarg[1] = nil; pos = 0; prog = (byte*)typ->gc[1]; @@@ -1871,14 -1678,18 +1684,18 @@@ } // Unrolls GC program in typ->gc[1] into typ->gc[0] - static void - unrollgcprog(Type *typ) + void + runtime·unrollgcprog_m(void) { - static Lock lock; + static Mutex lock; + Type *typ; byte *mask, *prog; uintptr pos; - uint32 x; + uintptr x; + typ = g->m->ptrarg[0]; + g->m->ptrarg[0] = nil; + runtime·lock(&lock); mask = (byte*)typ->gc[0]; if(mask[0] == 0) { @@@ -2119,8 -1830,8 +1838,8 @@@ runtime·getgcmask(byte *p, Type *t, by *len = n/PtrSize; *mask = runtime·mallocgc(*len, nil, 0); for(i = 0; i < n; i += PtrSize) { - off = (p+i-data)/PtrSize; - bits = (work.gcdata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; + off = (p+i-runtime·data)/PtrSize; - bits = (((byte*)runtime·gcdatamask.data)[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; ++ bits = (runtime·gcdatamask.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; (*mask)[i/PtrSize] = bits; } return; @@@ -2131,8 -1842,8 +1850,8 @@@ *len = n/PtrSize; *mask = runtime·mallocgc(*len, nil, 0); for(i = 0; i < n; i += PtrSize) { - off = (p+i-bss)/PtrSize; - bits = (work.gcbss[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; + off = (p+i-runtime·bss)/PtrSize; - bits = (((byte*)runtime·gcbssmask.data)[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; ++ bits = (runtime·gcbssmask.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; (*mask)[i/PtrSize] = bits; } return; @@@ -2180,8 -1892,8 +1900,8 @@@ *len = n/PtrSize; *mask = runtime·mallocgc(*len, nil, 0); for(i = 0; i < n; i += PtrSize) { - off = (p+i-frame.varp+size)/PtrSize; + off = (p+i-(byte*)frame.varp+size)/PtrSize; - bits = (bv.data[off*BitsPerPointer/32] >> ((off*BitsPerPointer)%32))&BitsMask; + bits = (bv.bytedata[off/PointersPerByte] >> ((off%PointersPerByte)*BitsPerPointer))&BitsMask; (*mask)[i/PtrSize] = bits; } } diff --cc src/pkg/runtime/mgc0.h index 99271a532b,d04b5cab8f..cc7acfbe1f --- a/src/pkg/runtime/mgc0.h +++ b/src/pkg/runtime/mgc0.h @@@ -7,16 -7,9 +7,16 @@@ enum { ScanStackByFrames = 1, + // TODO(rsc): Half the code in the garbage collector + // now accesses the bitmap as an array of bytes + // instead of as an array of uintptrs. + // This is tricky to do correctly in a portable fashion. + // (It breaks on big-endian systems.) + // Should we just make the bitmap a byte array? + // Four bits per word (see #defines below). - wordsPerBitmapWord = sizeof(void*)*8/4, gcBits = 4, + wordsPerBitmapByte = 8/gcBits, // GC type info programs. // The programs allow to store type info required for GC in a compact form. diff --cc src/pkg/runtime/panic.go index 0000000000,1e35561d15..75d155b3fc mode 000000,100644..100644 --- a/src/pkg/runtime/panic.go +++ b/src/pkg/runtime/panic.go @@@ -1,0 -1,216 +1,216 @@@ + // 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" + + var indexError = error(errorString("index out of range")) + + func panicindex() { + panic(indexError) + } + + var sliceError = error(errorString("slice bounds out of range")) + + func panicslice() { + panic(sliceError) + } + + var divideError = error(errorString("integer divide by zero")) + + func panicdivide() { + panic(divideError) + } + + func throwreturn() { + gothrow("no return at end of a typed function - compiler is broken") + } + + func throwinit() { + gothrow("recursive call during initialization - linker skew") + } + + // Create a new deferred function fn with siz bytes of arguments. + // The compiler turns a defer statement into a call to this. + //go:nosplit + func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn + // the arguments of fn are in a perilous state. The stack map + // for deferproc does not describe them. So we can't let garbage + // collection or stack copying trigger until we've copied them out + // to somewhere safe. deferproc_m does that. Until deferproc_m, + // we can only call nosplit routines. + argp := uintptr(unsafe.Pointer(&fn)) + argp += unsafe.Sizeof(fn) - if GOARCH == "arm" { ++ if GOARCH == "arm" || GOARCH == "power64" || GOARCH == "power64le" { + argp += ptrSize // skip caller's saved link register + } + mp := acquirem() + mp.scalararg[0] = uintptr(siz) + mp.ptrarg[0] = unsafe.Pointer(fn) + mp.scalararg[1] = argp + mp.scalararg[2] = getcallerpc(unsafe.Pointer(&siz)) + + if mp.curg != getg() { + // go code on the m stack can't defer + gothrow("defer on m") + } + + onM(deferproc_m) + + releasem(mp) + + // deferproc returns 0 normally. + // a deferred func that stops a panic + // makes the deferproc return 1. + // the code the compiler generates always + // checks the return value and jumps to the + // end of the function if deferproc returns != 0. + return0() + // No code can go here - the C return register has + // been set and must not be clobbered. + } + + // Each P holds pool for defers with arg sizes 8, 24, 40, 56 and 72 bytes. + // Memory block is 40 (24 for 32 bits) bytes larger due to Defer header. + // This maps exactly to malloc size classes. + + // defer size class for arg size sz + func deferclass(siz uintptr) uintptr { + return (siz + 7) >> 4 + } + + // total size of memory block for defer with arg size sz + func totaldefersize(siz uintptr) uintptr { + return (unsafe.Sizeof(_defer{}) - unsafe.Sizeof(_defer{}.args)) + round(siz, ptrSize) + } + + // Ensure that defer arg sizes that map to the same defer size class + // also map to the same malloc size class. + func testdefersizes() { + var m [len(p{}.deferpool)]int32 + + for i := range m { + m[i] = -1 + } + for i := uintptr(0); ; i++ { + defersc := deferclass(i) + if defersc >= uintptr(len(m)) { + break + } + siz := goroundupsize(totaldefersize(i)) + if m[defersc] < 0 { + m[defersc] = int32(siz) + continue + } + if m[defersc] != int32(siz) { + print("bad defer size class: i=", i, " siz=", siz, " defersc=", defersc, "\n") + gothrow("bad defer size class") + } + } + } + + // Allocate a Defer, usually using per-P pool. + // Each defer must be released with freedefer. + // Note: runs on M stack + func newdefer(siz int32) *_defer { + var d *_defer + sc := deferclass(uintptr(siz)) + mp := acquirem() + if sc < uintptr(len(p{}.deferpool)) { + pp := mp.p + d = pp.deferpool[sc] + if d != nil { + pp.deferpool[sc] = d.link + } + } + if d == nil { + // deferpool is empty or just a big defer + total := goroundupsize(totaldefersize(uintptr(siz))) + d = (*_defer)(gomallocgc(total, conservative, 0)) + } + d.siz = siz + d.special = false + gp := mp.curg + d.link = gp._defer + gp._defer = d + releasem(mp) + return d + } + + // Free the given defer. + // The defer cannot be used after this call. + func freedefer(d *_defer) { + if d.special { + return + } + sc := deferclass(uintptr(d.siz)) + if sc < uintptr(len(p{}.deferpool)) { + mp := acquirem() + pp := mp.p + d.link = pp.deferpool[sc] + pp.deferpool[sc] = d + releasem(mp) + // No need to wipe out pointers in argp/pc/fn/args, + // because we empty the pool before GC. + } + } + + // Run a deferred function if there is one. + // The compiler inserts a call to this at the end of any + // function which calls defer. + // If there is a deferred function, this will call runtime·jmpdefer, + // which will jump to the deferred function such that it appears + // to have been called by the caller of deferreturn at the point + // just before deferreturn was called. The effect is that deferreturn + // is called again and again until there are no more deferred functions. + // Cannot split the stack because we reuse the caller's frame to + // call the deferred function. + + // The single argument isn't actually used - it just has its address + // taken so it can be matched against pending defers. + //go:nosplit + func deferreturn(arg0 uintptr) { + gp := getg() + d := gp._defer + if d == nil { + return + } + argp := uintptr(unsafe.Pointer(&arg0)) + if d.argp != argp { + return + } + + // Moving arguments around. + // Do not allow preemption here, because the garbage collector + // won't know the form of the arguments until the jmpdefer can + // flip the PC over to fn. + mp := acquirem() + memmove(unsafe.Pointer(argp), unsafe.Pointer(&d.args), uintptr(d.siz)) + fn := d.fn + gp._defer = d.link + freedefer(d) + releasem(mp) + jmpdefer(fn, argp) + } + + // Goexit terminates the goroutine that calls it. No other goroutine is affected. + // Goexit runs all deferred calls before terminating the goroutine. + // + // Calling Goexit from the main goroutine terminates that goroutine + // without func main returning. Since func main has not returned, + // the program continues execution of other goroutines. + // If all other goroutines exit, the program crashes. + func Goexit() { + // Run all deferred functions for the current goroutine. + gp := getg() + for gp._defer != nil { + d := gp._defer + gp._defer = d.link + reflectcall(unsafe.Pointer(d.fn), unsafe.Pointer(&d.args), uint32(d.siz), uint32(d.siz)) + freedefer(d) + // Note: we ignore recovers here because Goexit isn't a panic + } + goexit() + } diff --cc src/pkg/runtime/panic1.go index 0000000000,e877434320..9faae8a4b1 mode 000000,100644..100644 --- a/src/pkg/runtime/panic1.go +++ b/src/pkg/runtime/panic1.go @@@ -1,0 -1,209 +1,209 @@@ + // 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 runtime + + import "unsafe" + + // Print all currently active panics. Used when crashing. + func printpanics(p *_panic) { + if p.link != nil { + printpanics(p.link) + print("\t") + } + print("panic: ") + printany(p.arg) + if p.recovered { + print(" [recovered]") + } + print("\n") + } + + // The implementation of the predeclared function panic. + func gopanic(e interface{}) { + gp := getg() + if gp.m.curg != gp { + gothrow("panic on m stack") + } + var p _panic + var dabort _defer + p.arg = e + p.link = gp._panic + gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) + + fn := abortpanic + dabort.fn = *(**funcval)(unsafe.Pointer(&fn)) + dabort.siz = ptrSize + dabort.args[0] = noescape((unsafe.Pointer)(&p)) // TODO(khr): why do I need noescape here? + dabort.argp = _NoArgs + dabort.special = true + + for { + d := gp._defer + if d == nil { + break + } + // take defer off list in case of recursive panic + gp._defer = d.link + argp := unsafe.Pointer(d.argp) // must be pointer so it gets adjusted during stack copy + pc := d.pc + + // The deferred function may cause another panic, + // so reflectcall may not return. Set up a defer + // to mark this panic aborted if that happens. + dabort.link = gp._defer + gp._defer = (*_defer)(noescape(unsafe.Pointer(&dabort))) + p._defer = d + + p.argp = getargp(0) + reflectcall(unsafe.Pointer(d.fn), unsafe.Pointer(&d.args), uint32(d.siz), uint32(d.siz)) + p.argp = 0 + + // reflectcall did not panic. Remove dabort. + if gp._defer != &dabort { + gothrow("bad defer entry in panic") + } + gp._defer = dabort.link + + // trigger shrinkage to test stack copy. See stack_test.go:TestStackPanic + //GC() + + freedefer(d) + if p.recovered { + gp._panic = p.link + // Aborted panics are marked but remain on the g.panic list. + // Remove them from the list and free the associated defers. + for gp._panic != nil && gp._panic.aborted { + freedefer(gp._panic._defer) + gp._panic = gp._panic.link + } + if gp._panic == nil { // must be done with signal + gp.sig = 0 + } + // Pass information about recovering frame to recovery. + gp.sigcode0 = uintptr(argp) + gp.sigcode1 = pc + mcall(recovery_m) + gothrow("recovery failed") // mcall should not return + } + } + + // ran out of deferred calls - old-school panic now + startpanic() + printpanics(gp._panic) + dopanic(0) // should not return + *(*int)(nil) = 0 // not reached + } + + // getargp returns the location where the caller + // writes outgoing function call arguments. + //go:nosplit + func getargp(x int) uintptr { + // x is an argument mainly so that we can return its address. + // However, we need to make the function complex enough + // that it won't be inlined. We always pass x = 0, so this code + // does nothing other than keep the compiler from thinking + // the function is simple enough to inline. + if x > 0 { + return getcallersp(unsafe.Pointer(&x)) * 0 + } + return uintptr(noescape(unsafe.Pointer(&x))) + } + + func abortpanic(p *_panic) { + p.aborted = true + } + + // The implementation of the predeclared function recover. + // Cannot split the stack because it needs to reliably + // find the stack segment of its caller. + // + // TODO(rsc): Once we commit to CopyStackAlways, + // this doesn't need to be nosplit. + //go:nosplit + func gorecover(argp uintptr) interface{} { + // Must be in a function running as part of a deferred call during the panic. + // Must be called from the topmost function of the call + // (the function used in the defer statement). + // p.argp is the argument pointer of that topmost deferred function call. + // Compare against argp reported by caller. + // If they match, the caller is the one who can recover. + gp := getg() + p := gp._panic + if p != nil && !p.recovered && argp == p.argp { + p.recovered = true + return p.arg + } + return nil + } + + //go:nosplit + func startpanic() { + onM(startpanic_m) + } + + //go:nosplit + func dopanic(unused int) { + gp := getg() + mp := acquirem() + mp.ptrarg[0] = unsafe.Pointer(gp) + mp.scalararg[0] = getcallerpc((unsafe.Pointer)(&unused)) + mp.scalararg[1] = getcallersp((unsafe.Pointer)(&unused)) + onM(dopanic_m) // should never return + *(*int)(nil) = 0 + } + + //go:nosplit + func throw(s *byte) { ++ print("fatal error: ", gostringnocopy(s), "\n") + gp := getg() + if gp.m.throwing == 0 { + gp.m.throwing = 1 + } + startpanic() - print("fatal error: ", gostringnocopy(s), "\n") + dopanic(0) + *(*int)(nil) = 0 // not reached + } + + //go:nosplit + func gothrow(s string) { + gp := getg() + if gp.m.throwing == 0 { + gp.m.throwing = 1 + } + startpanic() + print("fatal error: ", s, "\n") + dopanic(0) + *(*int)(nil) = 0 // not reached + } + + func panicstring(s *int8) { + // m.softfloat is set during software floating point, + // which might cause a fault during a memory load. + // It increments m.locks to avoid preemption. + // If we're panicking, the software floating point frames + // will be unwound, so decrement m.locks as they would. + gp := getg() + if gp.m.softfloat != 0 { + gp.m.locks-- + gp.m.softfloat = 0 + } + + if gp.m.mallocing != 0 { + print("panic: ", s, "\n") + gothrow("panic during malloc") + } + if gp.m.gcing != 0 { + print("panic: ", s, "\n") + gothrow("panic during gc") + } + if gp.m.locks != 0 { + print("panic: ", s, "\n") + gothrow("panic holding locks") + } + + var err interface{} + newErrorCString(unsafe.Pointer(s), &err) + gopanic(err) + } diff --cc src/pkg/runtime/proc.c index 23513e1a7b,698be9ffae..d77f20abf9 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@@ -1832,8 -2244,9 +2244,9 @@@ voi runtime·newproc(int32 siz, FuncVal* fn, ...) { byte *argp; + void (*mfn)(void); - if(thechar == '5') + if(thechar == '5' || thechar == '9') argp = (byte*)(&fn+2); // skip caller's saved LR else argp = (byte*)(&fn+1); diff --cc src/pkg/runtime/stack.c index 88f24408ba,18b3f40648..b85a844840 --- a/src/pkg/runtime/stack.c +++ b/src/pkg/runtime/stack.c @@@ -567,23 -601,12 +601,12 @@@ adjustpointers(byte **scanp, BitVector } break; case BitsMultiWord: - switch(bv->data[(i+1) / (32 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 31) & 3) { + switch(bv->bytedata[(i+1) / (8 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 7) & 3) { - case BitsString: - // string referents are never on the stack, never need to be adjusted - i++; // skip len - break; - case BitsSlice: - p = scanp[i]; - if(minp <= p && p < maxp) { - if(StackDebug >= 3) - runtime·printf("adjust slice %p\n", p); - scanp[i] = p + delta; - } - i += 2; // skip len, cap - break; + default: + runtime·throw("unexpected garbage collection bits"); case BitsEface: t = (Type*)scanp[i]; - if(t != nil && (t->size > PtrSize || (t->kind & KindNoPointers) == 0)) { + if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) { p = scanp[i+1]; if(minp <= p && p < maxp) { if(StackDebug >= 3) @@@ -969,7 -1045,8 +1045,8 @@@ runtime·newstack(void while(dst < dstend) *dst++ = *src++; } + - if(thechar == '5') { + if(thechar == '5' || thechar == '9') { // caller would have saved its LR below args. sp -= sizeof(void*); *(void**)sp = nil; diff --cc src/pkg/runtime/thunk.s index 0000000000,babc927680..8cd9afe608 mode 000000,100644..100644 --- a/src/pkg/runtime/thunk.s +++ b/src/pkg/runtime/thunk.s @@@ -1,0 -1,150 +1,156 @@@ + // 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. + + // This file exposes various internal runtime functions to other packages in std lib. + + #include "zasm_GOOS_GOARCH.h" + #include "textflag.h" + + #ifdef GOARCH_arm + #define JMP B + #endif ++#ifdef GOARCH_power64 ++#define JMP BR ++#endif ++#ifdef GOARCH_power64le ++#define JMP BR ++#endif + + TEXT net·runtimeNano(SB),NOSPLIT,$0-0 + JMP runtime·nanotime(SB) + + TEXT time·runtimeNano(SB),NOSPLIT,$0-0 + JMP runtime·nanotime(SB) + + TEXT time·Sleep(SB),NOSPLIT,$0-0 + JMP runtime·timeSleep(SB) + + TEXT time·startTimer(SB),NOSPLIT,$0-0 + JMP runtime·startTimer(SB) + + TEXT time·stopTimer(SB),NOSPLIT,$0-0 + JMP runtime·stopTimer(SB) + + TEXT sync·runtime_Syncsemacquire(SB),NOSPLIT,$0-0 + JMP runtime·syncsemacquire(SB) + + TEXT sync·runtime_Syncsemrelease(SB),NOSPLIT,$0-0 + JMP runtime·syncsemrelease(SB) + + TEXT sync·runtime_Syncsemcheck(SB),NOSPLIT,$0-0 + JMP runtime·syncsemcheck(SB) + + TEXT sync·runtime_Semacquire(SB),NOSPLIT,$0-0 + JMP runtime·asyncsemacquire(SB) + + TEXT sync·runtime_Semrelease(SB),NOSPLIT,$0-0 + JMP runtime·asyncsemrelease(SB) + + TEXT sync·runtime_registerPoolCleanup(SB),NOSPLIT,$0-0 + JMP runtime·registerPoolCleanup(SB) + + TEXT net·runtime_Semacquire(SB),NOSPLIT,$0-0 + JMP runtime·asyncsemacquire(SB) + + TEXT net·runtime_Semrelease(SB),NOSPLIT,$0-0 + JMP runtime·asyncsemrelease(SB) + + TEXT runtime∕pprof·runtime_cyclesPerSecond(SB),NOSPLIT,$0-0 + JMP runtime·tickspersecond(SB) + + TEXT bytes·Compare(SB),NOSPLIT,$0-0 + JMP runtime·cmpbytes(SB) + + TEXT runtime·reflectcall(SB), NOSPLIT, $0-0 + JMP reflect·call(SB) + + TEXT reflect·chanclose(SB), NOSPLIT, $0-0 + JMP runtime·closechan(SB) + + TEXT reflect·chanlen(SB), NOSPLIT, $0-0 + JMP runtime·reflect_chanlen(SB) + + TEXT reflect·chancap(SB), NOSPLIT, $0-0 + JMP runtime·reflect_chancap(SB) + + TEXT reflect·chansend(SB), NOSPLIT, $0-0 + JMP runtime·reflect_chansend(SB) + + TEXT reflect·chanrecv(SB), NOSPLIT, $0-0 + JMP runtime·reflect_chanrecv(SB) + + TEXT runtime∕debug·freeOSMemory(SB), NOSPLIT, $0-0 + JMP runtime·freeOSMemory(SB) + + TEXT net·runtime_pollServerInit(SB),NOSPLIT,$0-0 + JMP runtime·netpollServerInit(SB) + + TEXT net·runtime_pollOpen(SB),NOSPLIT,$0-0 + JMP runtime·netpollOpen(SB) + + TEXT net·runtime_pollClose(SB),NOSPLIT,$0-0 + JMP runtime·netpollClose(SB) + + TEXT net·runtime_pollReset(SB),NOSPLIT,$0-0 + JMP runtime·netpollReset(SB) + + TEXT net·runtime_pollWait(SB),NOSPLIT,$0-0 + JMP runtime·netpollWait(SB) + + TEXT net·runtime_pollWaitCanceled(SB),NOSPLIT,$0-0 + JMP runtime·netpollWaitCanceled(SB) + + TEXT net·runtime_pollSetDeadline(SB),NOSPLIT,$0-0 + JMP runtime·netpollSetDeadline(SB) + + TEXT net·runtime_pollUnblock(SB),NOSPLIT,$0-0 + JMP runtime·netpollUnblock(SB) + + TEXT syscall·setenv_c(SB), NOSPLIT, $0-0 + JMP runtime·syscall_setenv_c(SB) + + TEXT reflect·makemap(SB),NOSPLIT,$0-0 + JMP runtime·reflect_makemap(SB) + + TEXT reflect·mapaccess(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapaccess(SB) + + TEXT reflect·mapassign(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapassign(SB) + + TEXT reflect·mapdelete(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapdelete(SB) + + TEXT reflect·mapiterinit(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapiterinit(SB) + + TEXT reflect·mapiterkey(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapiterkey(SB) + + TEXT reflect·mapiternext(SB),NOSPLIT,$0-0 + JMP runtime·reflect_mapiternext(SB) + + TEXT reflect·maplen(SB),NOSPLIT,$0-0 + JMP runtime·reflect_maplen(SB) + + TEXT reflect·ismapkey(SB),NOSPLIT,$0-0 + JMP runtime·reflect_ismapkey(SB) + + TEXT reflect·ifaceE2I(SB),NOSPLIT,$0-0 + JMP runtime·reflect_ifaceE2I(SB) + + TEXT reflect·unsafe_New(SB),NOSPLIT,$0-0 + JMP runtime·newobject(SB) + + TEXT reflect·unsafe_NewArray(SB),NOSPLIT,$0-0 + JMP runtime·newarray(SB) + + TEXT reflect·makechan(SB),NOSPLIT,$0-0 + JMP runtime·makechan(SB) + + TEXT reflect·rselect(SB), NOSPLIT, $0-0 + JMP runtime·reflect_rselect(SB) + + TEXT os·sigpipe(SB), NOSPLIT, $0-0 + JMP runtime·os_sigpipe(SB) diff --cc test/nosplit.go index b5399ad38d,c9d008acd3..8dab2fc7a7 --- a/test/nosplit.go +++ b/test/nosplit.go @@@ -264,7 -255,15 +264,15 @@@ TestCases } name := m[1] size, _ := strconv.Atoi(m[2]) + + // The limit was originally 128 but is now 384. + // Instead of rewriting the test cases above, adjust + // the first stack frame to use up the extra 32 bytes. + if i == 0 { + size += 384 - 128 + } + - if goarch == "amd64" && size%8 == 4 { + if size%ptrSize == 4 { continue TestCases } nosplit := m[3]