cmd/compile: implement unsafe.Add and unsafe.Slice

Updates #19367.
Updates #40481.

Change-Id: Iabd2afdd0d520e5d68fd9e6dedd013335a4b3886
Reviewed-on: https://go-review.googlesource.com/c/go/+/312214
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Trust: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Matthew Dempsky 2021-04-21 02:11:15 -07:00
parent 0d32d9e8a8
commit fadad851a3
20 changed files with 439 additions and 204 deletions

View File

@ -677,7 +677,7 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) {
n := n.(*ir.UnaryExpr) n := n.(*ir.UnaryExpr)
e.discard(n.X) e.discard(n.X)
case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY: case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
e.call([]hole{k}, n, nil) e.call([]hole{k}, n, nil)
case ir.ONEW: case ir.ONEW:
@ -1101,6 +1101,11 @@ func (e *escape) call(ks []hole, call, where ir.Node) {
case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
call := call.(*ir.UnaryExpr) call := call.(*ir.UnaryExpr)
argument(e.discardHole(), call.X) argument(e.discardHole(), call.X)
case ir.OUNSAFEADD, ir.OUNSAFESLICE:
call := call.(*ir.BinaryExpr)
argument(ks[0], call.X)
argument(e.discardHole(), call.Y)
} }
} }

View File

@ -136,7 +136,7 @@ func (n *BinaryExpr) SetOp(op Op) {
panic(n.no("SetOp " + op.String())) panic(n.no("SetOp " + op.String()))
case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, case OADD, OADDSTR, OAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE,
OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR, OLSH, OLT, OMOD, OMUL, ONE, OOR, ORSH, OSUB, OXOR,
OCOPY, OCOMPLEX, OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE,
OEFACE: OEFACE:
n.op = op n.op = op
} }

View File

@ -25,69 +25,71 @@ import (
// Op // Op
var OpNames = []string{ var OpNames = []string{
OADDR: "&", OADDR: "&",
OADD: "+", OADD: "+",
OADDSTR: "+", OADDSTR: "+",
OALIGNOF: "unsafe.Alignof", OALIGNOF: "unsafe.Alignof",
OANDAND: "&&", OANDAND: "&&",
OANDNOT: "&^", OANDNOT: "&^",
OAND: "&", OAND: "&",
OAPPEND: "append", OAPPEND: "append",
OAS: "=", OAS: "=",
OAS2: "=", OAS2: "=",
OBREAK: "break", OBREAK: "break",
OCALL: "function call", // not actual syntax OCALL: "function call", // not actual syntax
OCAP: "cap", OCAP: "cap",
OCASE: "case", OCASE: "case",
OCLOSE: "close", OCLOSE: "close",
OCOMPLEX: "complex", OCOMPLEX: "complex",
OBITNOT: "^", OBITNOT: "^",
OCONTINUE: "continue", OCONTINUE: "continue",
OCOPY: "copy", OCOPY: "copy",
ODELETE: "delete", ODELETE: "delete",
ODEFER: "defer", ODEFER: "defer",
ODIV: "/", ODIV: "/",
OEQ: "==", OEQ: "==",
OFALL: "fallthrough", OFALL: "fallthrough",
OFOR: "for", OFOR: "for",
OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892 OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892
OGE: ">=", OGE: ">=",
OGOTO: "goto", OGOTO: "goto",
OGT: ">", OGT: ">",
OIF: "if", OIF: "if",
OIMAG: "imag", OIMAG: "imag",
OINLMARK: "inlmark", OINLMARK: "inlmark",
ODEREF: "*", ODEREF: "*",
OLEN: "len", OLEN: "len",
OLE: "<=", OLE: "<=",
OLSH: "<<", OLSH: "<<",
OLT: "<", OLT: "<",
OMAKE: "make", OMAKE: "make",
ONEG: "-", ONEG: "-",
OMOD: "%", OMOD: "%",
OMUL: "*", OMUL: "*",
ONEW: "new", ONEW: "new",
ONE: "!=", ONE: "!=",
ONOT: "!", ONOT: "!",
OOFFSETOF: "unsafe.Offsetof", OOFFSETOF: "unsafe.Offsetof",
OOROR: "||", OOROR: "||",
OOR: "|", OOR: "|",
OPANIC: "panic", OPANIC: "panic",
OPLUS: "+", OPLUS: "+",
OPRINTN: "println", OPRINTN: "println",
OPRINT: "print", OPRINT: "print",
ORANGE: "range", ORANGE: "range",
OREAL: "real", OREAL: "real",
ORECV: "<-", ORECV: "<-",
ORECOVER: "recover", ORECOVER: "recover",
ORETURN: "return", ORETURN: "return",
ORSH: ">>", ORSH: ">>",
OSELECT: "select", OSELECT: "select",
OSEND: "<-", OSEND: "<-",
OSIZEOF: "unsafe.Sizeof", OSIZEOF: "unsafe.Sizeof",
OSUB: "-", OSUB: "-",
OSWITCH: "switch", OSWITCH: "switch",
OXOR: "^", OUNSAFEADD: "unsafe.Add",
OUNSAFESLICE: "unsafe.Slice",
OXOR: "^",
} }
// GoString returns the Go syntax for the Op, or else its name. // GoString returns the Go syntax for the Op, or else its name.
@ -218,6 +220,8 @@ var OpPrec = []int{
OTMAP: 8, OTMAP: 8,
OTSTRUCT: 8, OTSTRUCT: 8,
OTYPE: 8, OTYPE: 8,
OUNSAFEADD: 8,
OUNSAFESLICE: 8,
OINDEXMAP: 8, OINDEXMAP: 8,
OINDEX: 8, OINDEX: 8,
OSLICE: 8, OSLICE: 8,
@ -794,7 +798,7 @@ func exprFmt(n Node, s fmt.State, prec int) {
n := n.(*SliceHeaderExpr) n := n.(*SliceHeaderExpr)
fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap) fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap)
case OCOMPLEX, OCOPY: case OCOMPLEX, OCOPY, OUNSAFEADD, OUNSAFESLICE:
n := n.(*BinaryExpr) n := n.(*BinaryExpr)
fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y) fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y)

View File

@ -247,6 +247,8 @@ const (
OALIGNOF // unsafe.Alignof(X) OALIGNOF // unsafe.Alignof(X)
OOFFSETOF // unsafe.Offsetof(X) OOFFSETOF // unsafe.Offsetof(X)
OSIZEOF // unsafe.Sizeof(X) OSIZEOF // unsafe.Sizeof(X)
OUNSAFEADD // unsafe.Add(X, Y)
OUNSAFESLICE // unsafe.Slice(X, Y)
OMETHEXPR // method expression OMETHEXPR // method expression
// statements // statements

View File

@ -119,53 +119,55 @@ func _() {
_ = x[OALIGNOF-108] _ = x[OALIGNOF-108]
_ = x[OOFFSETOF-109] _ = x[OOFFSETOF-109]
_ = x[OSIZEOF-110] _ = x[OSIZEOF-110]
_ = x[OMETHEXPR-111] _ = x[OUNSAFEADD-111]
_ = x[OBLOCK-112] _ = x[OUNSAFESLICE-112]
_ = x[OBREAK-113] _ = x[OMETHEXPR-113]
_ = x[OCASE-114] _ = x[OBLOCK-114]
_ = x[OCONTINUE-115] _ = x[OBREAK-115]
_ = x[ODEFER-116] _ = x[OCASE-116]
_ = x[OFALL-117] _ = x[OCONTINUE-117]
_ = x[OFOR-118] _ = x[ODEFER-118]
_ = x[OFORUNTIL-119] _ = x[OFALL-119]
_ = x[OGOTO-120] _ = x[OFOR-120]
_ = x[OIF-121] _ = x[OFORUNTIL-121]
_ = x[OLABEL-122] _ = x[OGOTO-122]
_ = x[OGO-123] _ = x[OIF-123]
_ = x[ORANGE-124] _ = x[OLABEL-124]
_ = x[ORETURN-125] _ = x[OGO-125]
_ = x[OSELECT-126] _ = x[ORANGE-126]
_ = x[OSWITCH-127] _ = x[ORETURN-127]
_ = x[OTYPESW-128] _ = x[OSELECT-128]
_ = x[OFUNCINST-129] _ = x[OSWITCH-129]
_ = x[OTCHAN-130] _ = x[OTYPESW-130]
_ = x[OTMAP-131] _ = x[OFUNCINST-131]
_ = x[OTSTRUCT-132] _ = x[OTCHAN-132]
_ = x[OTINTER-133] _ = x[OTMAP-133]
_ = x[OTFUNC-134] _ = x[OTSTRUCT-134]
_ = x[OTARRAY-135] _ = x[OTINTER-135]
_ = x[OTSLICE-136] _ = x[OTFUNC-136]
_ = x[OINLCALL-137] _ = x[OTARRAY-137]
_ = x[OEFACE-138] _ = x[OTSLICE-138]
_ = x[OITAB-139] _ = x[OINLCALL-139]
_ = x[OIDATA-140] _ = x[OEFACE-140]
_ = x[OSPTR-141] _ = x[OITAB-141]
_ = x[OCFUNC-142] _ = x[OIDATA-142]
_ = x[OCHECKNIL-143] _ = x[OSPTR-143]
_ = x[OVARDEF-144] _ = x[OCFUNC-144]
_ = x[OVARKILL-145] _ = x[OCHECKNIL-145]
_ = x[OVARLIVE-146] _ = x[OVARDEF-146]
_ = x[ORESULT-147] _ = x[OVARKILL-147]
_ = x[OINLMARK-148] _ = x[OVARLIVE-148]
_ = x[OLINKSYMOFFSET-149] _ = x[ORESULT-149]
_ = x[OTAILCALL-150] _ = x[OINLMARK-150]
_ = x[OGETG-151] _ = x[OLINKSYMOFFSET-151]
_ = x[OEND-152] _ = x[OTAILCALL-152]
_ = x[OGETG-153]
_ = x[OEND-154]
} }
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND" const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 213, 216, 221, 228, 235, 241, 250, 258, 266, 272, 276, 285, 292, 296, 299, 306, 314, 321, 327, 330, 336, 343, 351, 355, 362, 370, 372, 374, 376, 378, 380, 382, 387, 392, 400, 403, 412, 415, 419, 427, 434, 443, 456, 459, 462, 465, 468, 471, 474, 480, 483, 486, 492, 496, 499, 503, 508, 513, 519, 524, 528, 533, 541, 549, 555, 564, 575, 582, 586, 593, 601, 605, 609, 613, 620, 627, 635, 641, 649, 654, 659, 663, 671, 676, 680, 683, 691, 695, 697, 702, 704, 709, 715, 721, 727, 733, 741, 746, 750, 757, 763, 768, 774, 780, 787, 792, 796, 801, 805, 810, 818, 824, 831, 838, 844, 851, 864, 872, 876, 879} var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 136, 138, 141, 151, 158, 165, 172, 176, 180, 188, 196, 205, 213, 216, 221, 228, 235, 241, 250, 258, 266, 272, 276, 285, 292, 296, 299, 306, 314, 321, 327, 330, 336, 343, 351, 355, 362, 370, 372, 374, 376, 378, 380, 382, 387, 392, 400, 403, 412, 415, 419, 427, 434, 443, 456, 459, 462, 465, 468, 471, 474, 480, 483, 486, 492, 496, 499, 503, 508, 513, 519, 524, 528, 533, 541, 549, 555, 564, 575, 582, 586, 593, 601, 605, 609, 613, 620, 627, 635, 641, 650, 661, 669, 674, 679, 683, 691, 696, 700, 703, 711, 715, 717, 722, 724, 729, 735, 741, 747, 753, 761, 766, 770, 777, 783, 788, 794, 800, 807, 812, 816, 821, 825, 830, 838, 844, 851, 858, 864, 871, 884, 892, 896, 899}
func (i Op) String() string { func (i Op) String() string {
if i >= Op(len(_Op_index)-1) { if i >= Op(len(_Op_index)-1) {

View File

@ -29,6 +29,14 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
} }
switch { switch {
case tv.IsBuiltin(): case tv.IsBuiltin():
// Qualified builtins, such as unsafe.Add and unsafe.Slice.
if expr, ok := expr.(*syntax.SelectorExpr); ok {
if name, ok := expr.X.(*syntax.Name); ok {
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
return g.use(expr.Sel)
}
}
}
return g.use(expr.(*syntax.Name)) return g.use(expr.(*syntax.Name))
case tv.IsType(): case tv.IsType():
return ir.TypeNode(g.typ(tv.Type)) return ir.TypeNode(g.typ(tv.Type))

View File

@ -795,11 +795,11 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
return u1 return u1
} }
case ir.OCOMPLEX, ir.OCOPY: case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
transformArgs(n) transformArgs(n)
b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1]) b := ir.NewBinaryExpr(n.Pos(), op, n.Args[0], n.Args[1])
n1 := typed(n.Type(), ir.InitExpr(n.Init(), b)) n1 := typed(n.Type(), ir.InitExpr(n.Init(), b))
if op == ir.OCOPY { if op != ir.OCOMPLEX {
// nothing more to do // nothing more to do
return n1 return n1
} }

View File

@ -3196,6 +3196,12 @@ func (s *state) expr(n ir.Node) *ssa.Value {
n := n.(*ir.UnaryExpr) n := n.(*ir.UnaryExpr)
return s.newObject(n.Type().Elem()) return s.newObject(n.Type().Elem())
case ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr)
ptr := s.expr(n.X)
len := s.expr(n.Y)
return s.newValue2(ssa.OpAddPtr, n.Type(), ptr, len)
default: default:
s.Fatalf("unhandled expr %v", n.Op()) s.Fatalf("unhandled expr %v", n.Op())
return nil return nil

View File

@ -136,69 +136,71 @@ var runtimeDecls = [...]struct {
{"makeslice64", funcTag, 113}, {"makeslice64", funcTag, 113},
{"makeslicecopy", funcTag, 114}, {"makeslicecopy", funcTag, 114},
{"growslice", funcTag, 116}, {"growslice", funcTag, 116},
{"memmove", funcTag, 117}, {"unsafeslice", funcTag, 117},
{"memclrNoHeapPointers", funcTag, 118}, {"unsafeslice64", funcTag, 118},
{"memclrHasPointers", funcTag, 118}, {"memmove", funcTag, 119},
{"memequal", funcTag, 119}, {"memclrNoHeapPointers", funcTag, 120},
{"memequal0", funcTag, 120}, {"memclrHasPointers", funcTag, 120},
{"memequal8", funcTag, 120}, {"memequal", funcTag, 121},
{"memequal16", funcTag, 120}, {"memequal0", funcTag, 122},
{"memequal32", funcTag, 120}, {"memequal8", funcTag, 122},
{"memequal64", funcTag, 120}, {"memequal16", funcTag, 122},
{"memequal128", funcTag, 120}, {"memequal32", funcTag, 122},
{"f32equal", funcTag, 121}, {"memequal64", funcTag, 122},
{"f64equal", funcTag, 121}, {"memequal128", funcTag, 122},
{"c64equal", funcTag, 121}, {"f32equal", funcTag, 123},
{"c128equal", funcTag, 121}, {"f64equal", funcTag, 123},
{"strequal", funcTag, 121}, {"c64equal", funcTag, 123},
{"interequal", funcTag, 121}, {"c128equal", funcTag, 123},
{"nilinterequal", funcTag, 121}, {"strequal", funcTag, 123},
{"memhash", funcTag, 122}, {"interequal", funcTag, 123},
{"memhash0", funcTag, 123}, {"nilinterequal", funcTag, 123},
{"memhash8", funcTag, 123}, {"memhash", funcTag, 124},
{"memhash16", funcTag, 123}, {"memhash0", funcTag, 125},
{"memhash32", funcTag, 123}, {"memhash8", funcTag, 125},
{"memhash64", funcTag, 123}, {"memhash16", funcTag, 125},
{"memhash128", funcTag, 123}, {"memhash32", funcTag, 125},
{"f32hash", funcTag, 123}, {"memhash64", funcTag, 125},
{"f64hash", funcTag, 123}, {"memhash128", funcTag, 125},
{"c64hash", funcTag, 123}, {"f32hash", funcTag, 125},
{"c128hash", funcTag, 123}, {"f64hash", funcTag, 125},
{"strhash", funcTag, 123}, {"c64hash", funcTag, 125},
{"interhash", funcTag, 123}, {"c128hash", funcTag, 125},
{"nilinterhash", funcTag, 123}, {"strhash", funcTag, 125},
{"int64div", funcTag, 124}, {"interhash", funcTag, 125},
{"uint64div", funcTag, 125}, {"nilinterhash", funcTag, 125},
{"int64mod", funcTag, 124}, {"int64div", funcTag, 126},
{"uint64mod", funcTag, 125}, {"uint64div", funcTag, 127},
{"float64toint64", funcTag, 126}, {"int64mod", funcTag, 126},
{"float64touint64", funcTag, 127}, {"uint64mod", funcTag, 127},
{"float64touint32", funcTag, 128}, {"float64toint64", funcTag, 128},
{"int64tofloat64", funcTag, 129}, {"float64touint64", funcTag, 129},
{"uint64tofloat64", funcTag, 130}, {"float64touint32", funcTag, 130},
{"uint32tofloat64", funcTag, 131}, {"int64tofloat64", funcTag, 131},
{"complex128div", funcTag, 132}, {"uint64tofloat64", funcTag, 132},
{"getcallerpc", funcTag, 133}, {"uint32tofloat64", funcTag, 133},
{"getcallersp", funcTag, 133}, {"complex128div", funcTag, 134},
{"getcallerpc", funcTag, 135},
{"getcallersp", funcTag, 135},
{"racefuncenter", funcTag, 31}, {"racefuncenter", funcTag, 31},
{"racefuncexit", funcTag, 9}, {"racefuncexit", funcTag, 9},
{"raceread", funcTag, 31}, {"raceread", funcTag, 31},
{"racewrite", funcTag, 31}, {"racewrite", funcTag, 31},
{"racereadrange", funcTag, 134}, {"racereadrange", funcTag, 136},
{"racewriterange", funcTag, 134}, {"racewriterange", funcTag, 136},
{"msanread", funcTag, 134}, {"msanread", funcTag, 136},
{"msanwrite", funcTag, 134}, {"msanwrite", funcTag, 136},
{"msanmove", funcTag, 135}, {"msanmove", funcTag, 137},
{"checkptrAlignment", funcTag, 136}, {"checkptrAlignment", funcTag, 138},
{"checkptrArithmetic", funcTag, 138}, {"checkptrArithmetic", funcTag, 140},
{"libfuzzerTraceCmp1", funcTag, 139}, {"libfuzzerTraceCmp1", funcTag, 141},
{"libfuzzerTraceCmp2", funcTag, 140}, {"libfuzzerTraceCmp2", funcTag, 142},
{"libfuzzerTraceCmp4", funcTag, 141}, {"libfuzzerTraceCmp4", funcTag, 143},
{"libfuzzerTraceCmp8", funcTag, 142}, {"libfuzzerTraceCmp8", funcTag, 144},
{"libfuzzerTraceConstCmp1", funcTag, 139}, {"libfuzzerTraceConstCmp1", funcTag, 141},
{"libfuzzerTraceConstCmp2", funcTag, 140}, {"libfuzzerTraceConstCmp2", funcTag, 142},
{"libfuzzerTraceConstCmp4", funcTag, 141}, {"libfuzzerTraceConstCmp4", funcTag, 143},
{"libfuzzerTraceConstCmp8", funcTag, 142}, {"libfuzzerTraceConstCmp8", funcTag, 144},
{"x86HasPOPCNT", varTag, 6}, {"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6}, {"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6}, {"x86HasFMA", varTag, 6},
@ -221,7 +223,7 @@ func params(tlist ...*types.Type) []*types.Field {
} }
func runtimeTypes() []*types.Type { func runtimeTypes() []*types.Type {
var typs [143]*types.Type var typs [145]*types.Type
typs[0] = types.ByteType typs[0] = types.ByteType
typs[1] = types.NewPtr(typs[0]) typs[1] = types.NewPtr(typs[0])
typs[2] = types.Types[types.TANY] typs[2] = types.Types[types.TANY]
@ -339,31 +341,33 @@ func runtimeTypes() []*types.Type {
typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7])) typs[114] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
typs[115] = types.NewSlice(typs[2]) typs[115] = types.NewSlice(typs[2])
typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115])) typs[116] = newSig(params(typs[1], typs[115], typs[15]), params(typs[115]))
typs[117] = newSig(params(typs[3], typs[3], typs[5]), nil) typs[117] = newSig(params(typs[1], typs[15]), nil)
typs[118] = newSig(params(typs[7], typs[5]), nil) typs[118] = newSig(params(typs[1], typs[22]), nil)
typs[119] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) typs[119] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[120] = newSig(params(typs[3], typs[3]), params(typs[6])) typs[120] = newSig(params(typs[7], typs[5]), nil)
typs[121] = newSig(params(typs[7], typs[7]), params(typs[6])) typs[121] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
typs[122] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5])) typs[122] = newSig(params(typs[3], typs[3]), params(typs[6]))
typs[123] = newSig(params(typs[7], typs[5]), params(typs[5])) typs[123] = newSig(params(typs[7], typs[7]), params(typs[6]))
typs[124] = newSig(params(typs[22], typs[22]), params(typs[22])) typs[124] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5]))
typs[125] = newSig(params(typs[24], typs[24]), params(typs[24])) typs[125] = newSig(params(typs[7], typs[5]), params(typs[5]))
typs[126] = newSig(params(typs[20]), params(typs[22])) typs[126] = newSig(params(typs[22], typs[22]), params(typs[22]))
typs[127] = newSig(params(typs[20]), params(typs[24])) typs[127] = newSig(params(typs[24], typs[24]), params(typs[24]))
typs[128] = newSig(params(typs[20]), params(typs[60])) typs[128] = newSig(params(typs[20]), params(typs[22]))
typs[129] = newSig(params(typs[22]), params(typs[20])) typs[129] = newSig(params(typs[20]), params(typs[24]))
typs[130] = newSig(params(typs[24]), params(typs[20])) typs[130] = newSig(params(typs[20]), params(typs[60]))
typs[131] = newSig(params(typs[60]), params(typs[20])) typs[131] = newSig(params(typs[22]), params(typs[20]))
typs[132] = newSig(params(typs[26], typs[26]), params(typs[26])) typs[132] = newSig(params(typs[24]), params(typs[20]))
typs[133] = newSig(nil, params(typs[5])) typs[133] = newSig(params(typs[60]), params(typs[20]))
typs[134] = newSig(params(typs[5], typs[5]), nil) typs[134] = newSig(params(typs[26], typs[26]), params(typs[26]))
typs[135] = newSig(params(typs[5], typs[5], typs[5]), nil) typs[135] = newSig(nil, params(typs[5]))
typs[136] = newSig(params(typs[7], typs[1], typs[5]), nil) typs[136] = newSig(params(typs[5], typs[5]), nil)
typs[137] = types.NewSlice(typs[7]) typs[137] = newSig(params(typs[5], typs[5], typs[5]), nil)
typs[138] = newSig(params(typs[7], typs[137]), nil) typs[138] = newSig(params(typs[7], typs[1], typs[5]), nil)
typs[139] = newSig(params(typs[64], typs[64]), nil) typs[139] = types.NewSlice(typs[7])
typs[140] = newSig(params(typs[58], typs[58]), nil) typs[140] = newSig(params(typs[7], typs[139]), nil)
typs[141] = newSig(params(typs[60], typs[60]), nil) typs[141] = newSig(params(typs[64], typs[64]), nil)
typs[142] = newSig(params(typs[24], typs[24]), nil) typs[142] = newSig(params(typs[58], typs[58]), nil)
typs[143] = newSig(params(typs[60], typs[60]), nil)
typs[144] = newSig(params(typs[24], typs[24]), nil)
return typs[:] return typs[:]
} }

View File

@ -183,6 +183,9 @@ func makeslice(typ *byte, len int, cap int) unsafe.Pointer
func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer func makeslice64(typ *byte, len int64, cap int64) unsafe.Pointer
func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer func makeslicecopy(typ *byte, tolen int, fromlen int, from unsafe.Pointer) unsafe.Pointer
func growslice(typ *byte, old []any, cap int) (ary []any) func growslice(typ *byte, old []any, cap int) (ary []any)
func unsafeslice(typ *byte, len int)
func unsafeslice64(typ *byte, len int64)
func memmove(to *any, frm *any, length uintptr) func memmove(to *any, frm *any, length uintptr)
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
func memclrHasPointers(ptr unsafe.Pointer, n uintptr) func memclrHasPointers(ptr unsafe.Pointer, n uintptr)

View File

@ -760,7 +760,9 @@ func anyCallOrChan(n ir.Node) bool {
ir.OPRINTN, ir.OPRINTN,
ir.OREAL, ir.OREAL,
ir.ORECOVER, ir.ORECOVER,
ir.ORECV: ir.ORECV,
ir.OUNSAFEADD,
ir.OUNSAFESLICE:
return true return true
} }
return false return false

View File

@ -430,7 +430,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg) u := ir.NewUnaryExpr(n.Pos(), l.BuiltinOp, arg)
return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init return typecheck(ir.InitExpr(n.Init(), u), top) // typecheckargs can add to old.Init
case ir.OCOMPLEX, ir.OCOPY: case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE:
typecheckargs(n) typecheckargs(n)
arg1, arg2, ok := needTwoArgs(n) arg1, arg2, ok := needTwoArgs(n)
if !ok { if !ok {
@ -977,3 +977,39 @@ func tcRecover(n *ir.CallExpr) ir.Node {
n.SetType(types.Types[types.TINTER]) n.SetType(types.Types[types.TINTER])
return n return n
} }
// tcUnsafeAdd typechecks an OUNSAFEADD node.
func tcUnsafeAdd(n *ir.BinaryExpr) *ir.BinaryExpr {
n.X = AssignConv(Expr(n.X), types.Types[types.TUNSAFEPTR], "argument to unsafe.Add")
n.Y = DefaultLit(Expr(n.Y), types.Types[types.TINT])
if n.X.Type() == nil || n.Y.Type() == nil {
n.SetType(nil)
return n
}
if !n.Y.Type().IsInteger() {
n.SetType(nil)
return n
}
n.SetType(n.X.Type())
return n
}
// tcUnsafeSlice typechecks an OUNSAFESLICE node.
func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
n.X = Expr(n.X)
n.Y = Expr(n.Y)
if n.X.Type() == nil || n.Y.Type() == nil {
n.SetType(nil)
return n
}
t := n.X.Type()
if !t.IsPtr() {
base.Errorf("first argument to unsafe.Slice must be pointer; have %L", t)
}
if !checkunsafeslice(&n.Y) {
n.SetType(nil)
return n
}
n.SetType(types.NewSlice(t.Elem()))
return n
}

View File

@ -1664,7 +1664,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.typ(n.Type()) w.typ(n.Type())
} }
case ir.OCOPY, ir.OCOMPLEX: case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE:
// treated like other builtin calls (see e.g., OREAL) // treated like other builtin calls (see e.g., OREAL)
n := n.(*ir.BinaryExpr) n := n.(*ir.BinaryExpr)
w.op(n.Op()) w.op(n.Op())

View File

@ -1269,10 +1269,10 @@ func (r *importReader) node() ir.Node {
} }
return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr()) return ir.NewConvExpr(r.pos(), op, r.typ(), r.expr())
case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN: case ir.OCOPY, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCAP, ir.OCLOSE, ir.ODELETE, ir.OLEN, ir.OMAKE, ir.ONEW, ir.OPANIC, ir.ORECOVER, ir.OPRINT, ir.OPRINTN, ir.OUNSAFEADD, ir.OUNSAFESLICE:
if go117ExportTypes { if go117ExportTypes {
switch op { switch op {
case ir.OCOPY, ir.OCOMPLEX: case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE:
n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr()) n := ir.NewBinaryExpr(r.pos(), op, r.expr(), r.expr())
n.SetType(r.typ()) n.SetType(r.typ())
return n return n

View File

@ -775,6 +775,14 @@ func typecheck1(n ir.Node, top int) ir.Node {
n := n.(*ir.CallExpr) n := n.(*ir.CallExpr)
return tcRecover(n) return tcRecover(n)
case ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr)
return tcUnsafeAdd(n)
case ir.OUNSAFESLICE:
n := n.(*ir.BinaryExpr)
return tcUnsafeSlice(n)
case ir.OCLOSURE: case ir.OCLOSURE:
n := n.(*ir.ClosureExpr) n := n.(*ir.ClosureExpr)
tcClosure(n, top) tcClosure(n, top)
@ -1934,6 +1942,35 @@ func checkmake(t *types.Type, arg string, np *ir.Node) bool {
return true return true
} }
// checkunsafeslice is like checkmake but for unsafe.Slice.
func checkunsafeslice(np *ir.Node) bool {
n := *np
if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
base.Errorf("non-integer len argument in unsafe.Slice - %v", n.Type())
return false
}
// Do range checks for constants before DefaultLit
// to avoid redundant "constant NNN overflows int" errors.
if n.Op() == ir.OLITERAL {
v := toint(n.Val())
if constant.Sign(v) < 0 {
base.Errorf("negative len argument in unsafe.Slice")
return false
}
if ir.ConstOverflow(v, types.Types[types.TINT]) {
base.Errorf("len argument too large in unsafe.Slice")
return false
}
}
// DefaultLit is necessary for non-constants too: n might be 1.1<<k.
n = DefaultLit(n, types.Types[types.TINT])
*np = n
return true
}
// markBreak marks control statements containing break statements with SetHasBreak(true). // markBreak marks control statements containing break statements with SetHasBreak(true).
func markBreak(fn *ir.Func) { func markBreak(fn *ir.Func) {
var labels map[*types.Sym]ir.Node var labels map[*types.Sym]ir.Node

View File

@ -85,9 +85,11 @@ var unsafeFuncs = [...]struct {
name string name string
op ir.Op op ir.Op
}{ }{
{"Add", ir.OUNSAFEADD},
{"Alignof", ir.OALIGNOF}, {"Alignof", ir.OALIGNOF},
{"Offsetof", ir.OOFFSETOF}, {"Offsetof", ir.OOFFSETOF},
{"Sizeof", ir.OSIZEOF}, {"Sizeof", ir.OSIZEOF},
{"Slice", ir.OUNSAFESLICE},
} }
// InitUniverse initializes the universe block. // InitUniverse initializes the universe block.

View File

@ -653,6 +653,45 @@ func walkRecover(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
return mkcall("gorecover", nn.Type(), init, fp) return mkcall("gorecover", nn.Type(), init, fp)
} }
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
len := safeExpr(n.Y, init)
fnname := "unsafeslice64"
argtype := types.Types[types.TINT64]
// Type checking guarantees that TIDEAL len/cap are positive and fit in an int.
// The case of len or cap overflow when converting TUINT or TUINTPTR to TINT
// will be handled by the negative range checks in unsafeslice during runtime.
if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
fnname = "unsafeslice"
argtype = types.Types[types.TINT]
}
t := n.Type()
// Call runtime.unsafeslice[64] to check that the length argument is
// non-negative and smaller than the max length allowed for the
// element type.
fn := typecheck.LookupRuntime(fnname)
init.Append(mkcall1(fn, nil, init, reflectdata.TypePtr(t.Elem()), typecheck.Conv(len, argtype)))
ptr := walkExpr(n.X, init)
c := ir.NewUnaryExpr(n.Pos(), ir.OCHECKNIL, ptr)
c.SetTypecheck(1)
init.Append(c)
// TODO(mdempsky): checkptr instrumentation. Maybe merge into length
// check above, along with nil check? Need to be careful about
// notinheap pointers though: can't pass them as unsafe.Pointer.
h := ir.NewSliceHeaderExpr(n.Pos(), t,
typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
typecheck.Conv(len, types.Types[types.TINT]),
typecheck.Conv(len, types.Types[types.TINT]))
return walkExpr(typecheck.Expr(h), init)
}
func badtype(op ir.Op, tl, tr *types.Type) { func badtype(op ir.Op, tl, tr *types.Type) {
var s string var s string
if tl != nil { if tl != nil {

View File

@ -117,12 +117,17 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n.X = walkExpr(n.X, init) n.X = walkExpr(n.X, init)
return n return n
case ir.OEFACE, ir.OAND, ir.OANDNOT, ir.OSUB, ir.OMUL, ir.OADD, ir.OOR, ir.OXOR, ir.OLSH, ir.ORSH: case ir.OEFACE, ir.OAND, ir.OANDNOT, ir.OSUB, ir.OMUL, ir.OADD, ir.OOR, ir.OXOR, ir.OLSH, ir.ORSH,
ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr) n := n.(*ir.BinaryExpr)
n.X = walkExpr(n.X, init) n.X = walkExpr(n.X, init)
n.Y = walkExpr(n.Y, init) n.Y = walkExpr(n.Y, init)
return n return n
case ir.OUNSAFESLICE:
n := n.(*ir.BinaryExpr)
return walkUnsafeSlice(n, init)
case ir.ODOT, ir.ODOTPTR: case ir.ODOT, ir.ODOTPTR:
n := n.(*ir.SelectorExpr) n := n.(*ir.SelectorExpr)
return walkDot(n, init) return walkDot(n, init)

View File

@ -112,6 +112,25 @@ func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {
return makeslice(et, len, cap) return makeslice(et, len, cap)
} }
func unsafeslice(et *_type, len int) {
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > maxAlloc || len < 0 {
panicunsafeslicelen()
}
}
func unsafeslice64(et *_type, len64 int64) {
len := int(len64)
if int64(len) != len64 {
panicunsafeslicelen()
}
unsafeslice(et, len)
}
func panicunsafeslicelen() {
panic(errorString("unsafe.Slice: len out of range"))
}
// growslice handles slice growth during append. // growslice handles slice growth during append.
// It is passed the slice element type, the old slice, and the desired new minimum capacity, // It is passed the slice element type, the old slice, and the desired new minimum capacity,
// and it returns a new slice with at least that capacity, with the old data // and it returns a new slice with at least that capacity, with the old data

61
test/unsafebuiltins.go Normal file
View File

@ -0,0 +1,61 @@
// run
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"math"
"unsafe"
)
const maxUintptr = 1 << (8 * unsafe.Sizeof(uintptr(0)))
func main() {
var p [10]byte
// unsafe.Add
{
p1 := unsafe.Pointer(&p[1])
assert(unsafe.Add(p1, 1) == unsafe.Pointer(&p[2]))
assert(unsafe.Add(p1, -1) == unsafe.Pointer(&p[0]))
}
// unsafe.Slice
{
s := unsafe.Slice(&p[0], len(p))
assert(&s[0] == &p[0])
assert(len(s) == len(p))
assert(cap(s) == len(p))
// nil pointer
mustPanic(func() { _ = unsafe.Slice((*int)(nil), 0) })
// negative length
var neg int = -1
mustPanic(func() { _ = unsafe.Slice(new(byte), neg) })
// length too large
var tooBig uint64 = math.MaxUint64
mustPanic(func() { _ = unsafe.Slice(new(byte), tooBig) })
// size overflows address space
mustPanic(func() { _ = unsafe.Slice(new(uint64), maxUintptr/8) })
mustPanic(func() { _ = unsafe.Slice(new(uint64), maxUintptr/8+1) })
}
}
func assert(ok bool) {
if !ok {
panic("FAIL")
}
}
func mustPanic(f func()) {
defer func() {
assert(recover() != nil)
}()
f()
}