diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go index b766864a306f82..7c087d2ab5f9c4 100644 --- a/src/cmd/compile/internal/escape/stmt.go +++ b/src/cmd/compile/internal/escape/stmt.go @@ -48,6 +48,22 @@ func (e *escape) stmt(n ir.Node) { e.dcl(n.X) } + case ir.ODCLGOLOCAL: + // Record loop depth at declaration. + n := n.(*ir.Decl) + if !ir.IsBlank(n.X) { + e.dcl(n.X) + // GoLocal variables must be escape + loc := e.oldLoc(n.X) + loc.attrs |= attrEscapes + } + + case ir.ODCLGOLOCALALLOC: + n := n.(*ir.Decl) + if !ir.IsBlank(n.X) { + e.dcl(n.X) + } + case ir.OLABEL: n := n.(*ir.LabelStmt) if n.Label.IsBlank() { diff --git a/src/cmd/compile/internal/ir/go_local_map.go b/src/cmd/compile/internal/ir/go_local_map.go new file mode 100644 index 00000000000000..4dd247f27165ee --- /dev/null +++ b/src/cmd/compile/internal/ir/go_local_map.go @@ -0,0 +1,14 @@ +package ir + +// goLocalInitMap is mapping need init virtual variable to its go_local variable. +var goLocalInitMap = map[*Name]*Name{} + +// BindGoLocalInit records need init virtual variable and its go_local variable. +func BindGoLocalInit(goLocal, needInit *Name) { + goLocalInitMap[needInit] = goLocal +} + +// GetGoLocalByInit gets go_local variable for need init virtual variable. +func GetGoLocalByInit(needInit *Name) *Name { + return goLocalInitMap[needInit] +} diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go index 21d181dba62ae4..5776a2b3cd3ac1 100644 --- a/src/cmd/compile/internal/ir/node.go +++ b/src/cmd/compile/internal/ir/node.go @@ -173,6 +173,12 @@ const ( OCOPY // copy(X, Y) ODCL // var X (declares X of type X.Type) + // ODCLGOLOCAL: go_local X (declares go local X of type X.Type). + // ODCLGOLOCALALLOC: go local alloc, this define a virtual variable + // recording if alloc new mem for go_local variable. + ODCLGOLOCAL + ODCLGOLOCALALLOC + // Used during parsing but don't last. ODCLFUNC // func f() or func (r) f() diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go index fb97ac68f45904..11daf0c83ed11e 100644 --- a/src/cmd/compile/internal/ir/op_string.go +++ b/src/cmd/compile/internal/ir/op_string.go @@ -56,115 +56,117 @@ func _() { _ = x[OCONVNOP-45] _ = x[OCOPY-46] _ = x[ODCL-47] - _ = x[ODCLFUNC-48] - _ = x[ODELETE-49] - _ = x[ODOT-50] - _ = x[ODOTPTR-51] - _ = x[ODOTMETH-52] - _ = x[ODOTINTER-53] - _ = x[OXDOT-54] - _ = x[ODOTTYPE-55] - _ = x[ODOTTYPE2-56] - _ = x[OEQ-57] - _ = x[ONE-58] - _ = x[OLT-59] - _ = x[OLE-60] - _ = x[OGE-61] - _ = x[OGT-62] - _ = x[ODEREF-63] - _ = x[OINDEX-64] - _ = x[OINDEXMAP-65] - _ = x[OKEY-66] - _ = x[OSTRUCTKEY-67] - _ = x[OLEN-68] - _ = x[OMAKE-69] - _ = x[OMAKECHAN-70] - _ = x[OMAKEMAP-71] - _ = x[OMAKESLICE-72] - _ = x[OMAKESLICECOPY-73] - _ = x[OMUL-74] - _ = x[ODIV-75] - _ = x[OMOD-76] - _ = x[OLSH-77] - _ = x[ORSH-78] - _ = x[OAND-79] - _ = x[OANDNOT-80] - _ = x[ONEW-81] - _ = x[ONOT-82] - _ = x[OBITNOT-83] - _ = x[OPLUS-84] - _ = x[ONEG-85] - _ = x[OOROR-86] - _ = x[OPANIC-87] - _ = x[OPRINT-88] - _ = x[OPRINTLN-89] - _ = x[OPAREN-90] - _ = x[OSEND-91] - _ = x[OSLICE-92] - _ = x[OSLICEARR-93] - _ = x[OSLICESTR-94] - _ = x[OSLICE3-95] - _ = x[OSLICE3ARR-96] - _ = x[OSLICEHEADER-97] - _ = x[OSTRINGHEADER-98] - _ = x[ORECOVER-99] - _ = x[ORECOVERFP-100] - _ = x[ORECV-101] - _ = x[ORUNESTR-102] - _ = x[OSELRECV2-103] - _ = x[OMIN-104] - _ = x[OMAX-105] - _ = x[OREAL-106] - _ = x[OIMAG-107] - _ = x[OCOMPLEX-108] - _ = x[OUNSAFEADD-109] - _ = x[OUNSAFESLICE-110] - _ = x[OUNSAFESLICEDATA-111] - _ = x[OUNSAFESTRING-112] - _ = x[OUNSAFESTRINGDATA-113] - _ = x[OMETHEXPR-114] - _ = x[OMETHVALUE-115] - _ = x[OBLOCK-116] - _ = x[OBREAK-117] - _ = x[OCASE-118] - _ = x[OCONTINUE-119] - _ = x[ODEFER-120] - _ = x[OFALL-121] - _ = x[OFOR-122] - _ = x[OGOTO-123] - _ = x[OIF-124] - _ = x[OLABEL-125] - _ = x[OGO-126] - _ = x[ORANGE-127] - _ = x[ORETURN-128] - _ = x[OSELECT-129] - _ = x[OSWITCH-130] - _ = x[OTYPESW-131] - _ = x[OINLCALL-132] - _ = x[OMAKEFACE-133] - _ = x[OITAB-134] - _ = x[OIDATA-135] - _ = x[OSPTR-136] - _ = x[OCFUNC-137] - _ = x[OCHECKNIL-138] - _ = x[ORESULT-139] - _ = x[OINLMARK-140] - _ = x[OLINKSYMOFFSET-141] - _ = x[OJUMPTABLE-142] - _ = x[OINTERFACESWITCH-143] - _ = x[ODYNAMICDOTTYPE-144] - _ = x[ODYNAMICDOTTYPE2-145] - _ = x[ODYNAMICTYPE-146] - _ = x[OTAILCALL-147] - _ = x[OGETG-148] - _ = x[OGETCALLERPC-149] - _ = x[OGETCALLERSP-150] - _ = x[OEND-151] + _ = x[ODCLGOLOCAL-48] + _ = x[ODCLGOLOCALALLOC-49] + _ = x[ODCLFUNC-50] + _ = x[ODELETE-51] + _ = x[ODOT-52] + _ = x[ODOTPTR-53] + _ = x[ODOTMETH-54] + _ = x[ODOTINTER-55] + _ = x[OXDOT-56] + _ = x[ODOTTYPE-57] + _ = x[ODOTTYPE2-58] + _ = x[OEQ-59] + _ = x[ONE-60] + _ = x[OLT-61] + _ = x[OLE-62] + _ = x[OGE-63] + _ = x[OGT-64] + _ = x[ODEREF-65] + _ = x[OINDEX-66] + _ = x[OINDEXMAP-67] + _ = x[OKEY-68] + _ = x[OSTRUCTKEY-69] + _ = x[OLEN-70] + _ = x[OMAKE-71] + _ = x[OMAKECHAN-72] + _ = x[OMAKEMAP-73] + _ = x[OMAKESLICE-74] + _ = x[OMAKESLICECOPY-75] + _ = x[OMUL-76] + _ = x[ODIV-77] + _ = x[OMOD-78] + _ = x[OLSH-79] + _ = x[ORSH-80] + _ = x[OAND-81] + _ = x[OANDNOT-82] + _ = x[ONEW-83] + _ = x[ONOT-84] + _ = x[OBITNOT-85] + _ = x[OPLUS-86] + _ = x[ONEG-87] + _ = x[OOROR-88] + _ = x[OPANIC-89] + _ = x[OPRINT-90] + _ = x[OPRINTLN-91] + _ = x[OPAREN-92] + _ = x[OSEND-93] + _ = x[OSLICE-94] + _ = x[OSLICEARR-95] + _ = x[OSLICESTR-96] + _ = x[OSLICE3-97] + _ = x[OSLICE3ARR-98] + _ = x[OSLICEHEADER-99] + _ = x[OSTRINGHEADER-100] + _ = x[ORECOVER-101] + _ = x[ORECOVERFP-102] + _ = x[ORECV-103] + _ = x[ORUNESTR-104] + _ = x[OSELRECV2-105] + _ = x[OMIN-106] + _ = x[OMAX-107] + _ = x[OREAL-108] + _ = x[OIMAG-109] + _ = x[OCOMPLEX-110] + _ = x[OUNSAFEADD-111] + _ = x[OUNSAFESLICE-112] + _ = x[OUNSAFESLICEDATA-113] + _ = x[OUNSAFESTRING-114] + _ = x[OUNSAFESTRINGDATA-115] + _ = x[OMETHEXPR-116] + _ = x[OMETHVALUE-117] + _ = x[OBLOCK-118] + _ = x[OBREAK-119] + _ = x[OCASE-120] + _ = x[OCONTINUE-121] + _ = x[ODEFER-122] + _ = x[OFALL-123] + _ = x[OFOR-124] + _ = x[OGOTO-125] + _ = x[OIF-126] + _ = x[OLABEL-127] + _ = x[OGO-128] + _ = x[ORANGE-129] + _ = x[ORETURN-130] + _ = x[OSELECT-131] + _ = x[OSWITCH-132] + _ = x[OTYPESW-133] + _ = x[OINLCALL-134] + _ = x[OMAKEFACE-135] + _ = x[OITAB-136] + _ = x[OIDATA-137] + _ = x[OSPTR-138] + _ = x[OCFUNC-139] + _ = x[OCHECKNIL-140] + _ = x[ORESULT-141] + _ = x[OINLMARK-142] + _ = x[OLINKSYMOFFSET-143] + _ = x[OJUMPTABLE-144] + _ = x[OINTERFACESWITCH-145] + _ = x[ODYNAMICDOTTYPE-146] + _ = x[ODYNAMICDOTTYPE2-147] + _ = x[ODYNAMICTYPE-148] + _ = x[OTAILCALL-149] + _ = x[OGETG-150] + _ = x[OGETCALLERPC-151] + _ = x[OGETCALLERSP-152] + _ = x[OEND-153] } -const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2MINMAXREALIMAGCOMPLEXUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWINLCALLMAKEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEINTERFACESWITCHDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" +const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLEARCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLGOLOCALDCLGOLOCALALLOCDCLFUNCDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTLNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2MINMAXREALIMAGCOMPLEXUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWINLCALLMAKEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEINTERFACESWITCHDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" -var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 294, 298, 301, 308, 314, 317, 323, 330, 338, 342, 349, 357, 359, 361, 363, 365, 367, 369, 374, 379, 387, 390, 399, 402, 406, 414, 421, 430, 443, 446, 449, 452, 455, 458, 461, 467, 470, 473, 479, 483, 486, 490, 495, 500, 506, 511, 515, 520, 528, 536, 542, 551, 562, 574, 581, 590, 594, 601, 609, 612, 615, 619, 623, 630, 639, 650, 665, 677, 693, 701, 710, 715, 720, 724, 732, 737, 741, 744, 748, 750, 755, 757, 762, 768, 774, 780, 786, 793, 801, 805, 810, 814, 819, 827, 833, 840, 853, 862, 877, 891, 906, 917, 925, 929, 940, 951, 954} +var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 129, 141, 143, 146, 156, 163, 170, 177, 181, 185, 193, 201, 210, 213, 218, 223, 230, 237, 243, 252, 260, 268, 274, 278, 287, 294, 298, 301, 311, 326, 333, 339, 342, 348, 355, 363, 367, 374, 382, 384, 386, 388, 390, 392, 394, 399, 404, 412, 415, 424, 427, 431, 439, 446, 455, 468, 471, 474, 477, 480, 483, 486, 492, 495, 498, 504, 508, 511, 515, 520, 525, 532, 537, 541, 546, 554, 562, 568, 577, 588, 600, 607, 616, 620, 627, 635, 638, 641, 645, 649, 656, 665, 676, 691, 703, 719, 727, 736, 741, 746, 750, 758, 763, 767, 770, 774, 776, 781, 783, 788, 794, 800, 806, 812, 819, 827, 831, 836, 840, 845, 853, 859, 866, 879, 888, 903, 917, 932, 943, 951, 955, 966, 977, 980} func (i Op) String() string { if i >= Op(len(_Op_index)-1) { diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go index 0801ecdd9e8722..84ae29734a0485 100644 --- a/src/cmd/compile/internal/ir/stmt.go +++ b/src/cmd/compile/internal/ir/stmt.go @@ -24,7 +24,7 @@ func NewDecl(pos src.XPos, op Op, x *Name) *Decl { switch op { default: panic("invalid Decl op " + op.String()) - case ODCL: + case ODCL, ODCLGOLOCAL, ODCLGOLOCALALLOC: n.op = op } return n diff --git a/src/cmd/compile/internal/ir/symtab.go b/src/cmd/compile/internal/ir/symtab.go index 202c4942dea86f..6afebab7cabbd5 100644 --- a/src/cmd/compile/internal/ir/symtab.go +++ b/src/cmd/compile/internal/ir/symtab.go @@ -13,46 +13,48 @@ import ( var Syms symsStruct type symsStruct struct { - AssertE2I *obj.LSym - AssertE2I2 *obj.LSym - AssertI2I *obj.LSym - AssertI2I2 *obj.LSym - Asanread *obj.LSym - Asanwrite *obj.LSym - CgoCheckMemmove *obj.LSym - CgoCheckPtrWrite *obj.LSym - CheckPtrAlignment *obj.LSym - Deferproc *obj.LSym - Deferprocat *obj.LSym - DeferprocStack *obj.LSym - Deferreturn *obj.LSym - Duffcopy *obj.LSym - Duffzero *obj.LSym - GCWriteBarrier [8]*obj.LSym - Goschedguarded *obj.LSym - Growslice *obj.LSym - InterfaceSwitch *obj.LSym - Memmove *obj.LSym - Msanread *obj.LSym - Msanwrite *obj.LSym - Msanmove *obj.LSym - Newobject *obj.LSym - Newproc *obj.LSym - Panicdivide *obj.LSym - Panicshift *obj.LSym - PanicdottypeE *obj.LSym - PanicdottypeI *obj.LSym - Panicnildottype *obj.LSym - Panicoverflow *obj.LSym - Racefuncenter *obj.LSym - Racefuncexit *obj.LSym - Raceread *obj.LSym - Racereadrange *obj.LSym - Racewrite *obj.LSym - Racewriterange *obj.LSym - TypeAssert *obj.LSym - WBZero *obj.LSym - WBMove *obj.LSym + AssertE2I *obj.LSym + AssertE2I2 *obj.LSym + AssertI2I *obj.LSym + AssertI2I2 *obj.LSym + Asanread *obj.LSym + Asanwrite *obj.LSym + CgoCheckMemmove *obj.LSym + CgoCheckPtrWrite *obj.LSym + CheckPtrAlignment *obj.LSym + Deferproc *obj.LSym + Deferprocat *obj.LSym + DeferprocStack *obj.LSym + Deferreturn *obj.LSym + Duffcopy *obj.LSym + Duffzero *obj.LSym + GCWriteBarrier [8]*obj.LSym + Goschedguarded *obj.LSym + Growslice *obj.LSym + InterfaceSwitch *obj.LSym + Memmove *obj.LSym + Msanread *obj.LSym + Msanwrite *obj.LSym + Msanmove *obj.LSym + Newobject *obj.LSym + NewGoLocalObject *obj.LSym + NewGoLocalObjectForStringKey *obj.LSym + Newproc *obj.LSym + Panicdivide *obj.LSym + Panicshift *obj.LSym + PanicdottypeE *obj.LSym + PanicdottypeI *obj.LSym + Panicnildottype *obj.LSym + Panicoverflow *obj.LSym + Racefuncenter *obj.LSym + Racefuncexit *obj.LSym + Raceread *obj.LSym + Racereadrange *obj.LSym + Racewrite *obj.LSym + Racewriterange *obj.LSym + TypeAssert *obj.LSym + WBZero *obj.LSym + WBMove *obj.LSym // Wasm SigPanic *obj.LSym Staticuint64s *obj.LSym diff --git a/src/cmd/compile/internal/noder/codes.go b/src/cmd/compile/internal/noder/codes.go index 8bdbfc9a8800b8..2b6bcdebbaa6b2 100644 --- a/src/cmd/compile/internal/noder/codes.go +++ b/src/cmd/compile/internal/noder/codes.go @@ -20,6 +20,7 @@ const ( stmtSend stmtAssign stmtAssignOp + stmtGoLocalAssign stmtIncDec stmtBranch stmtCall diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index 58fbb72f5d23f8..ab92ec954bcfda 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1686,6 +1686,47 @@ func (r *reader) stmt1(tag codeStmt, out *ir.Nodes) ir.Node { n.Def = r.initDefn(n, names) return n + case stmtGoLocalAssign: + pos := r.pos() + names, lhs := r.assignList() + rhs := r.multiExpr() + // We rewrite go_local stmt to a group stmts: + // before: + // go_local a int = initA() + // after: + // &a, a_need_init := runtime.newGoLocal(key, type(int)) + // if a_need_init { + // a = initA() + // } + if len(names) == 0 { + return nil + } + + for _, name := range names { + out.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCLGOLOCAL, name))) + } + + if len(rhs) == 0 { + return nil + } + sym := &types.Sym{Pkg: names[0].Sym().Pkg, Name: "_compile_only_" + names[0].Sym().Name + "_need_init"} + needInit := names[0].Curfn.NewLocal(pos, sym, types.Types[types.TBOOL]) + ir.BindGoLocalInit(names[0], needInit) + out.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCLGOLOCALALLOC, needInit))) + + then := ir.Nodes{} + + if len(lhs) == 1 && len(rhs) == 1 { + n := ir.NewAssignStmt(pos, lhs[0], rhs[0]) + then.Append(n) + } else { + n := ir.NewAssignListStmt(pos, ir.OAS2, lhs, rhs) + then.Append(n) + } + + ifStmt := ir.NewIfStmt(pos, typecheck.Expr(needInit), then, nil) + return ifStmt + case stmtAssignOp: op := r.op() lhs := r.expr() diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index 8fed138a4a1202..79e3742264c632 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -1290,7 +1290,7 @@ func (w *writer) stmt1(stmt syntax.Stmt) { w.implicitConvExpr(typ, stmt.Rhs) default: - w.assignStmt(stmt, stmt.Lhs, stmt.Rhs) + w.assignStmt(stmt, stmt.Lhs, stmt.Rhs, 0) } case *syntax.BlockStmt: @@ -1408,16 +1408,20 @@ func (w *writer) declStmt(decl syntax.Decl) { case *syntax.ConstDecl, *syntax.TypeDecl: case *syntax.VarDecl: - w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values) + w.assignStmt(decl, namesAsExpr(decl.NameList), decl.Values, decl.Tok) } } // assignStmt writes out an assignment for "lhs = rhs". -func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr) { +func (w *writer) assignStmt(pos poser, lhs0, rhs0 syntax.Expr, tok syntax.Token) { lhs := syntax.UnpackListExpr(lhs0) rhs := syntax.UnpackListExpr(rhs0) - w.Code(stmtAssign) + if tok == syntax.GoLocal { + w.Code(stmtGoLocalAssign) + } else { + w.Code(stmtAssign) + } w.pos(pos) // As if w.assignList(lhs0). diff --git a/src/cmd/compile/internal/ssagen/go_local_map.go b/src/cmd/compile/internal/ssagen/go_local_map.go new file mode 100644 index 00000000000000..4627e215aa32d8 --- /dev/null +++ b/src/cmd/compile/internal/ssagen/go_local_map.go @@ -0,0 +1,9 @@ +package ssagen + +import ( + "cmd/compile/internal/ir" + "cmd/compile/internal/ssa" +) + +// goLocalAllocMap is mapping go_local variable to its need init ssa value. +var goLocalAllocMap = map[*ir.Name]*ssa.Value{} diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index 26d236dcacafb4..55fcd02602f492 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -127,6 +127,9 @@ func InitConfig() { ir.Syms.Asanread = typecheck.LookupRuntimeFunc("asanread") ir.Syms.Asanwrite = typecheck.LookupRuntimeFunc("asanwrite") ir.Syms.Newobject = typecheck.LookupRuntimeFunc("newobject") + ir.Syms.NewGoLocalObject = typecheck.LookupRuntimeFunc("newGoLocalObject") + ir.Syms.NewGoLocalObjectForStringKey = + typecheck.LookupRuntimeFunc("newGoLocalObjectForStringKey") ir.Syms.Newproc = typecheck.LookupRuntimeFunc("newproc") ir.Syms.Panicdivide = typecheck.LookupRuntimeFunc("panicdivide") ir.Syms.PanicdottypeE = typecheck.LookupRuntimeFunc("panicdottypeE") @@ -683,6 +686,29 @@ func (s *state) paramsToHeap() { do(typ.Results()) } +// newGoLocal gets heap memory for n and the flag indicating if alloc new mem. +func (s *state) newGoLocal(n *ir.Name) { + // We use variable name + pos as the unique key + key := n.Sym().Name + "@" + src.FmtGoLocalKey(n.Pos()) + ssaGoLocal, ssaAlloc := s.newGoLocalObject(key, n.Type(), nil) + goLocalAllocMap[n] = ssaAlloc + s.setHeapaddr(n.Pos(), n, ssaGoLocal) +} + +// newGoLocalAlloc set the value for the virtual variable indicating +// if alloc new mem for go_local variable. +func (s *state) newGoLocalAlloc(n *ir.Name) { + goLocal := ir.GetGoLocalByInit(n) + if goLocal == nil { + base.FatalfAt(n.Pos(), "cannot find go local variable for %v", n) + } + ssaAlloc := goLocalAllocMap[goLocal] + if ssaAlloc == nil { + base.FatalfAt(n.Pos(), "go local variable %v not decl", goLocal) + } + s.assign(n, ssaAlloc, false, 0) +} + // newHeapaddr allocates heap memory for n and sets its heap address. func (s *state) newHeapaddr(n *ir.Name) { s.setHeapaddr(n.Pos(), n, s.newObject(n.Type(), nil)) @@ -720,6 +746,20 @@ func (s *state) newObject(typ *types.Type, rtype *ssa.Value) *ssa.Value { return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, rtype)[0] } +// newGoLocalObject returns an SSA value denoting new(typ) and an SSA value(bool flag) indicating if alloc mem. +func (s *state) newGoLocalObject(key string, typ *types.Type, rtype *ssa.Value) (*ssa.Value, *ssa.Value) { + if typ.Size() == 0 { + return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb), s.constBool(false) + } + if rtype == nil { + rtype = s.reflectType(typ) + } + ssaKey := s.entryNewValue1A(ssa.OpConstString, types.Types[types.TSTRING], ssa.StringToAux(key), s.sb) + values := s.rtcall(ir.Syms.NewGoLocalObjectForStringKey, true, + []*types.Type{types.NewPtr(typ), types.Types[types.TBOOL]}, ssaKey, rtype) + return values[0], values[1] +} + func (s *state) checkPtrAlignment(n *ir.ConvExpr, v *ssa.Value, count *ssa.Value) { if !n.Type().IsPtr() { s.Fatalf("expected pointer type: %v", n.Type()) @@ -1561,6 +1601,14 @@ func (s *state) stmt(n ir.Node) { s.newHeapaddr(v) } + case ir.ODCLGOLOCAL: + n := n.(*ir.Decl) + s.newGoLocal(n.X) + + case ir.ODCLGOLOCALALLOC: + n := n.(*ir.Decl) + s.newGoLocalAlloc(n.X) + case ir.OLABEL: n := n.(*ir.LabelStmt) sym := n.Label diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index de277fc3d8cdab..0a710846c36e67 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -95,6 +95,7 @@ type ( NameList []*Name Type Expr // nil means no type Values Expr // nil means no values + Tok Token decl } diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index 66570fe92a823b..4317a4b7c4fc58 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -748,7 +748,12 @@ func (p *parser) varDecl(group *Group) Decl { if trace { defer p.trace("varDecl")() } + d := p.innerVarDecl(group) + d.Tok = _Var + return d +} +func (p *parser) innerVarDecl(group *Group) *VarDecl { d := new(VarDecl) d.pos = p.pos() d.Group = group @@ -763,7 +768,16 @@ func (p *parser) varDecl(group *Group) Decl { d.Values = p.exprList() } } + return d +} +// GoLocalSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) . +func (p *parser) goLocalDecl(group *Group) Decl { + if trace { + defer p.trace("goLocalDecl")() + } + d := p.innerVarDecl(group) + d.Tok = _GoLocal return d } @@ -2570,6 +2584,9 @@ func (p *parser) stmtOrNil() Stmt { case _Var: return p.declStmt(p.varDecl) + case _GoLocal: + return p.declStmt(p.goLocalDecl) + case _Const: return p.declStmt(p.constDecl) diff --git a/src/cmd/compile/internal/syntax/printer.go b/src/cmd/compile/internal/syntax/printer.go index 9f20db54dea5b9..eb44d7c042a3cb 100644 --- a/src/cmd/compile/internal/syntax/printer.go +++ b/src/cmd/compile/internal/syntax/printer.go @@ -816,7 +816,7 @@ func groupFor(d Decl) (token, *Group) { case *TypeDecl: return _Type, d.Group case *VarDecl: - return _Var, d.Group + return d.Tok, d.Group case *FuncDecl: return _Func, nil default: diff --git a/src/cmd/compile/internal/syntax/scanner.go b/src/cmd/compile/internal/syntax/scanner.go index 807d8383866dcb..4548dccf63d318 100644 --- a/src/cmd/compile/internal/syntax/scanner.go +++ b/src/cmd/compile/internal/syntax/scanner.go @@ -381,7 +381,7 @@ func (s *scanner) ident() { // possibly a keyword lit := s.segment() if len(lit) >= 2 { - if tok := keywordMap[hash(lit)]; tok != 0 && tokStrFast(tok) == string(lit) { + if tok := keywordMap[string(lit)]; tok != 0 && tokStrFast(tok) == string(lit) { s.nlsemi = contains(1<<_Break|1<<_Continue|1<<_Fallthrough|1<<_Return, tok) s.tok = tok return @@ -415,22 +415,12 @@ func (s *scanner) atIdentChar(first bool) bool { return true } -// hash is a perfect hash function for keywords. -// It assumes that s has at least length 2. -func hash(s []byte) uint { - return (uint(s[0])<<4 ^ uint(s[1]) + uint(len(s))) & uint(len(keywordMap)-1) -} - -var keywordMap [1 << 6]token // size must be power of two +var keywordMap map[string]token = make(map[string]token, 1<<6) // size must be power of two func init() { // populate keywordMap for tok := _Break; tok <= _Var; tok++ { - h := hash([]byte(tok.String())) - if keywordMap[h] != 0 { - panic("imperfect hash") - } - keywordMap[h] = tok + keywordMap[tok.String()] = tok } } diff --git a/src/cmd/compile/internal/syntax/token_string.go b/src/cmd/compile/internal/syntax/token_string.go index ef295eb24b2bc9..27c72f84ad7d99 100644 --- a/src/cmd/compile/internal/syntax/token_string.go +++ b/src/cmd/compile/internal/syntax/token_string.go @@ -53,13 +53,14 @@ func _() { _ = x[_Struct-43] _ = x[_Switch-44] _ = x[_Type-45] - _ = x[_Var-46] - _ = x[tokenCount-47] + _ = x[_GoLocal-46] + _ = x[_Var-47] + _ = x[tokenCount-48] } -const _token_name = "EOFnameliteralopop=opop=:=<-*([{)]},;:....breakcasechanconstcontinuedefaultdeferelsefallthroughforfuncgogotoifimportinterfacemappackagerangereturnselectstructswitchtypevar" +const _token_name = "EOFnameliteralopop=opop=:=<-*([{)]},;:....breakcasechanconstcontinuedefaultdeferelsefallthroughforfuncgogotoifimportinterfacemappackagerangereturnselectstructswitchtypego_localvar" -var _token_index = [...]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 171, 171} +var _token_index = [...]uint8{0, 3, 7, 14, 16, 19, 23, 24, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 42, 47, 51, 55, 60, 68, 75, 80, 84, 95, 98, 102, 104, 108, 110, 116, 125, 128, 135, 140, 146, 152, 158, 164, 168, 176, 179, 179} func (i token) String() string { i -= 1 diff --git a/src/cmd/compile/internal/syntax/tokens.go b/src/cmd/compile/internal/syntax/tokens.go index b08f699582fb65..b85ac89d866a28 100644 --- a/src/cmd/compile/internal/syntax/tokens.go +++ b/src/cmd/compile/internal/syntax/tokens.go @@ -66,6 +66,7 @@ const ( _Struct // struct _Switch // switch _Type // type + _GoLocal // go_local _Var // var // empty line comment to exclude it from .String @@ -82,6 +83,10 @@ const ( // for CallStmt Go = _Go Defer = _Defer + + // for VarDeclStmt + Var = _Var + GoLocal = _GoLocal ) // Make sure we have at most 64 tokens so we can use them in a set. diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go index ec849e315400c3..cd2e09a3125ef3 100644 --- a/src/cmd/compile/internal/typecheck/typecheck.go +++ b/src/cmd/compile/internal/typecheck/typecheck.go @@ -522,6 +522,8 @@ func typecheck1(n ir.Node, top int) ir.Node { case ir.OBREAK, ir.OCONTINUE, ir.ODCL, + ir.ODCLGOLOCAL, + ir.ODCLGOLOCALALLOC, ir.OGOTO, ir.OFALL: return n diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index de180a4a8d7797..87de97b0a3f306 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -764,6 +764,8 @@ func (o *orderState) stmt(n ir.Node) { case ir.OBREAK, ir.OCONTINUE, ir.ODCL, + ir.ODCLGOLOCAL, + ir.ODCLGOLOCALALLOC, ir.OFALL, ir.OGOTO, ir.OLABEL, diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go index b2a226e0789fbb..7e4da27b31db07 100644 --- a/src/cmd/compile/internal/walk/stmt.go +++ b/src/cmd/compile/internal/walk/stmt.go @@ -90,6 +90,8 @@ func walkStmt(n ir.Node) ir.Node { ir.OJUMPTABLE, ir.OINTERFACESWITCH, ir.ODCL, + ir.ODCLGOLOCAL, + ir.ODCLGOLOCALALLOC, ir.OCHECKNIL: return n diff --git a/src/cmd/internal/src/xpos.go b/src/cmd/internal/src/xpos.go index a74505997d2cfd..8efb789d01d351 100644 --- a/src/cmd/internal/src/xpos.go +++ b/src/cmd/internal/src/xpos.go @@ -7,6 +7,8 @@ package src +import "fmt" + // XPos is a more compact representation of Pos. type XPos struct { index int32 @@ -181,3 +183,8 @@ func (t *PosTable) FileTable() []string { } return fileLUT } + +// FmtGoLocalKey converts XPos to string, for the unique key of go_local variable +func FmtGoLocalKey(pos XPos) string { + return fmt.Sprintf("%v@%v", pos.lico, pos.index) +} diff --git a/src/go/token/token.go b/src/go/token/token.go index aa5d6e02a6f287..69216be09571b6 100644 --- a/src/go/token/token.go +++ b/src/go/token/token.go @@ -122,6 +122,7 @@ const ( STRUCT SWITCH TYPE + GOLOCAL VAR keyword_end @@ -224,11 +225,12 @@ var tokens = [...]string{ RANGE: "range", RETURN: "return", - SELECT: "select", - STRUCT: "struct", - SWITCH: "switch", - TYPE: "type", - VAR: "var", + SELECT: "select", + STRUCT: "struct", + SWITCH: "switch", + TYPE: "type", + GOLOCAL: "go_local", + VAR: "var", TILDE: "~", } @@ -291,6 +293,11 @@ func init() { // Lookup maps an identifier to its keyword token or [IDENT] (if not a keyword). func Lookup(ident string) Token { if tok, is_keyword := keywords[ident]; is_keyword { + // GoLocal stmt has the same syntax with Var stmt + // so we can return VAR to check syntax + if tok == GOLOCAL { + return VAR + } return tok } return IDENT diff --git a/src/runtime/go_local.go b/src/runtime/go_local.go new file mode 100644 index 00000000000000..d23d1f579e64ab --- /dev/null +++ b/src/runtime/go_local.go @@ -0,0 +1,48 @@ +package runtime + +import ( + "internal/abi" + "unsafe" +) + +type GoLocalHolder[T any] struct { + Val T +} + +type _InnerGoLocalKey[T any] struct { + rawKey any + v0 *T +} + +// newGoLocalObject creates a go_local object and record it. +func newGoLocalObject(key any, typ *_type) (pObject unsafe.Pointer, alloc bool) { + gp := getg() + ptr, ok := gp.localTable[key] + if ok { + return ptr, false + } + if gp.localTable == nil { + gp.localTable = map[any]unsafe.Pointer{} + } + ptr = mallocgc(typ.Size_, typ, true) + gp.localTable[key] = ptr + return ptr, true +} + +// newGoLocalObjectForStringKey wraps newGoLocalObject for ssa calling. +func newGoLocalObjectForStringKey(key string, typ *_type) (pObject unsafe.Pointer, alloc bool) { + return newGoLocalObject(key, typ) +} + +// NewGoLocal creates a go local object for rawKey + type and returns its holder. +// This can use the same one object in multiple places by the same rawKey + type +func NewGoLocal[T any](rawKey any, initFunc func() T) (ptrHolder *GoLocalHolder[T], alloc bool) { + key := _InnerGoLocalKey[T]{rawKey: rawKey, v0: nil} + wrapper0 := (*GoLocalHolder[T])(nil) + ptr, alloc := newGoLocalObject(key, abi.TypeOf(wrapper0).Elem()) + ptrHolder = (*GoLocalHolder[T])(ptr) + if alloc && initFunc != nil { + ptrHolder.Val = initFunc() + } + return ptrHolder, alloc +} diff --git a/src/runtime/proc.go b/src/runtime/proc.go index c4f175b0b76b22..f4a9f488f0417d 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -4998,6 +4998,9 @@ func newproc1(fn *funcval, callergp *g, callerpc uintptr, parked bool, waitreaso throw("newproc1: new g is not Gdead") } + // reset localTable when create or reuse a goroutine + newg.localTable = map[any]unsafe.Pointer{} + totalSize := uintptr(4*goarch.PtrSize + sys.MinFrameSize) // extra space in case of reads slightly beyond frame totalSize = alignUp(totalSize, sys.StackAlign) sp := newg.stack.hi - totalSize diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 4a789639611fb7..24573fb39a8a0a 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -529,6 +529,9 @@ type g struct { // and check for debt in the malloc hot path. The assist ratio // determines how this corresponds to scan work debt. gcAssistBytes int64 + + // localTable records GoLocal variables in this goroutine + localTable map[any]unsafe.Pointer } // gTrackingPeriod is the number of transitions out of _Grunning between