1
0
mirror of https://github.com/golang/go synced 2024-11-26 00:17:58 -07:00

cmd/compile: add support for unsafe.{String,StringData,SliceData}

For #53003

Change-Id: I13a761daca8b433b271a1feb711c103d9820772d
Reviewed-on: https://go-review.googlesource.com/c/go/+/423774
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: hopehook <hopehook@golangcn.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
cuiweixie 2022-08-16 17:52:13 +08:00 committed by Matthew Dempsky
parent 301ca7513f
commit c708532936
29 changed files with 842 additions and 414 deletions

View File

@ -180,11 +180,11 @@ func (e *escape) callCommon(ks []hole, call ir.Node, init *ir.Nodes, wrapper *ir
argument(e.discardHole(), &call.Args[i]) argument(e.discardHole(), &call.Args[i])
} }
case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE: case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
call := call.(*ir.UnaryExpr) call := call.(*ir.UnaryExpr)
argument(e.discardHole(), &call.X) argument(e.discardHole(), &call.X)
case ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
call := call.(*ir.BinaryExpr) call := call.(*ir.BinaryExpr)
argument(ks[0], &call.X) argument(ks[0], &call.X)
argument(e.discardHole(), &call.Y) argument(e.discardHole(), &call.Y)

View File

@ -134,7 +134,9 @@ 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.OINLCALL, ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER, ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OCALLMETH, ir.OCALLFUNC, ir.OCALLINTER, ir.OINLCALL,
ir.OLEN, ir.OCAP, ir.OCOMPLEX, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.OCOPY, ir.ORECOVER,
ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
e.call([]hole{k}, n) e.call([]hole{k}, n)
case ir.ONEW: case ir.ONEW:

View File

@ -137,7 +137,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, OUNSAFEADD, OUNSAFESLICE, OCOPY, OCOMPLEX, OUNSAFEADD, OUNSAFESLICE, OUNSAFESTRING,
OEFACE: OEFACE:
n.op = op n.op = op
} }
@ -624,6 +624,21 @@ func NewSliceHeaderExpr(pos src.XPos, typ *types.Type, ptr, len, cap Node) *Slic
return n return n
} }
// A StringHeaderExpr expression constructs a string header from its parts.
type StringHeaderExpr struct {
miniExpr
Ptr Node
Len Node
}
func NewStringHeaderExpr(pos src.XPos, ptr, len Node) *StringHeaderExpr {
n := &StringHeaderExpr{Ptr: ptr, Len: len}
n.pos = pos
n.op = OSTRINGHEADER
n.typ = types.Types[types.TSTRING]
return n
}
// A StarExpr is a dereference expression *X. // A StarExpr is a dereference expression *X.
// It may end up being a value or a type. // It may end up being a value or a type.
type StarExpr struct { type StarExpr struct {
@ -734,7 +749,8 @@ func (n *UnaryExpr) SetOp(op Op) {
case OBITNOT, ONEG, ONOT, OPLUS, ORECV, case OBITNOT, ONEG, ONOT, OPLUS, ORECV,
OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW, OALIGNOF, OCAP, OCLOSE, OIMAG, OLEN, ONEW,
OOFFSETOF, OPANIC, OREAL, OSIZEOF, OOFFSETOF, OPANIC, OREAL, OSIZEOF,
OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR: OCHECKNIL, OCFUNC, OIDATA, OITAB, OSPTR,
OUNSAFESTRINGDATA, OUNSAFESLICEDATA:
n.op = op n.op = op
} }
} }

View File

@ -88,6 +88,9 @@ var OpNames = []string{
OSWITCH: "switch", OSWITCH: "switch",
OUNSAFEADD: "unsafe.Add", OUNSAFEADD: "unsafe.Add",
OUNSAFESLICE: "unsafe.Slice", OUNSAFESLICE: "unsafe.Slice",
OUNSAFESLICEDATA: "unsafe.SliceData",
OUNSAFESTRING: "unsafe.String",
OUNSAFESTRINGDATA: "unsafe.StringData",
OXOR: "^", OXOR: "^",
} }
@ -212,6 +215,9 @@ var OpPrec = []int{
OTYPE: 8, OTYPE: 8,
OUNSAFEADD: 8, OUNSAFEADD: 8,
OUNSAFESLICE: 8, OUNSAFESLICE: 8,
OUNSAFESLICEDATA: 8,
OUNSAFESTRING: 8,
OUNSAFESTRINGDATA: 8,
OINDEXMAP: 8, OINDEXMAP: 8,
OINDEX: 8, OINDEX: 8,
OSLICE: 8, OSLICE: 8,
@ -220,6 +226,7 @@ var OpPrec = []int{
OSLICE3: 8, OSLICE3: 8,
OSLICE3ARR: 8, OSLICE3ARR: 8,
OSLICEHEADER: 8, OSLICEHEADER: 8,
OSTRINGHEADER: 8,
ODOTINTER: 8, ODOTINTER: 8,
ODOTMETH: 8, ODOTMETH: 8,
ODOTPTR: 8, ODOTPTR: 8,

View File

@ -233,6 +233,7 @@ const (
OSLICE3 // X[Low : High : Max] (X is untypedchecked or slice) OSLICE3 // X[Low : High : Max] (X is untypedchecked or slice)
OSLICE3ARR // X[Low : High : Max] (X is pointer to array) OSLICE3ARR // X[Low : High : Max] (X is pointer to array)
OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity) OSLICEHEADER // sliceheader{Ptr, Len, Cap} (Ptr is unsafe.Pointer, Len is length, Cap is capacity)
OSTRINGHEADER // stringheader{Ptr, Len} (Ptr is unsafe.Pointer, Len is length)
ORECOVER // recover() ORECOVER // recover()
ORECOVERFP // recover(Args) w/ explicit FP argument ORECOVERFP // recover(Args) w/ explicit FP argument
ORECV // <-X ORECV // <-X
@ -246,6 +247,9 @@ const (
OSIZEOF // unsafe.Sizeof(X) OSIZEOF // unsafe.Sizeof(X)
OUNSAFEADD // unsafe.Add(X, Y) OUNSAFEADD // unsafe.Add(X, Y)
OUNSAFESLICE // unsafe.Slice(X, Y) OUNSAFESLICE // unsafe.Slice(X, Y)
OUNSAFESLICEDATA // unsafe.SliceData(X)
OUNSAFESTRING // unsafe.String(X, Y)
OUNSAFESTRINGDATA // unsafe.StringData(X)
OMETHEXPR // X(Args) (method expression T.Method(args), first argument is the method receiver) OMETHEXPR // X(Args) (method expression T.Method(args), first argument is the method receiver)
OMETHVALUE // X.Sel (method expression t.Method, not called) OMETHVALUE // X.Sel (method expression t.Method, not called)

View File

@ -1146,6 +1146,34 @@ func (n *StarExpr) editChildren(edit func(Node) Node) {
} }
} }
func (n *StringHeaderExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *StringHeaderExpr) copy() Node {
c := *n
c.init = copyNodes(c.init)
return &c
}
func (n *StringHeaderExpr) doChildren(do func(Node) bool) bool {
if doNodes(n.init, do) {
return true
}
if n.Ptr != nil && do(n.Ptr) {
return true
}
if n.Len != nil && do(n.Len) {
return true
}
return false
}
func (n *StringHeaderExpr) editChildren(edit func(Node) Node) {
editNodes(n.init, edit)
if n.Ptr != nil {
n.Ptr = edit(n.Ptr).(Node)
}
if n.Len != nil {
n.Len = edit(n.Len).(Node)
}
}
func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) } func (n *StructKeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *StructKeyExpr) copy() Node { func (n *StructKeyExpr) copy() Node {
c := *n c := *n

View File

@ -107,62 +107,66 @@ func _() {
_ = x[OSLICE3-96] _ = x[OSLICE3-96]
_ = x[OSLICE3ARR-97] _ = x[OSLICE3ARR-97]
_ = x[OSLICEHEADER-98] _ = x[OSLICEHEADER-98]
_ = x[ORECOVER-99] _ = x[OSTRINGHEADER-99]
_ = x[ORECOVERFP-100] _ = x[ORECOVER-100]
_ = x[ORECV-101] _ = x[ORECOVERFP-101]
_ = x[ORUNESTR-102] _ = x[ORECV-102]
_ = x[OSELRECV2-103] _ = x[ORUNESTR-103]
_ = x[OREAL-104] _ = x[OSELRECV2-104]
_ = x[OIMAG-105] _ = x[OREAL-105]
_ = x[OCOMPLEX-106] _ = x[OIMAG-106]
_ = x[OALIGNOF-107] _ = x[OCOMPLEX-107]
_ = x[OOFFSETOF-108] _ = x[OALIGNOF-108]
_ = x[OSIZEOF-109] _ = x[OOFFSETOF-109]
_ = x[OUNSAFEADD-110] _ = x[OSIZEOF-110]
_ = x[OUNSAFESLICE-111] _ = x[OUNSAFEADD-111]
_ = x[OMETHEXPR-112] _ = x[OUNSAFESLICE-112]
_ = x[OMETHVALUE-113] _ = x[OUNSAFESLICEDATA-113]
_ = x[OBLOCK-114] _ = x[OUNSAFESTRING-114]
_ = x[OBREAK-115] _ = x[OUNSAFESTRINGDATA-115]
_ = x[OCASE-116] _ = x[OMETHEXPR-116]
_ = x[OCONTINUE-117] _ = x[OMETHVALUE-117]
_ = x[ODEFER-118] _ = x[OBLOCK-118]
_ = x[OFALL-119] _ = x[OBREAK-119]
_ = x[OFOR-120] _ = x[OCASE-120]
_ = x[OGOTO-121] _ = x[OCONTINUE-121]
_ = x[OIF-122] _ = x[ODEFER-122]
_ = x[OLABEL-123] _ = x[OFALL-123]
_ = x[OGO-124] _ = x[OFOR-124]
_ = x[ORANGE-125] _ = x[OGOTO-125]
_ = x[ORETURN-126] _ = x[OIF-126]
_ = x[OSELECT-127] _ = x[OLABEL-127]
_ = x[OSWITCH-128] _ = x[OGO-128]
_ = x[OTYPESW-129] _ = x[ORANGE-129]
_ = x[OFUNCINST-130] _ = x[ORETURN-130]
_ = x[OINLCALL-131] _ = x[OSELECT-131]
_ = x[OEFACE-132] _ = x[OSWITCH-132]
_ = x[OITAB-133] _ = x[OTYPESW-133]
_ = x[OIDATA-134] _ = x[OFUNCINST-134]
_ = x[OSPTR-135] _ = x[OINLCALL-135]
_ = x[OCFUNC-136] _ = x[OEFACE-136]
_ = x[OCHECKNIL-137] _ = x[OITAB-137]
_ = x[ORESULT-138] _ = x[OIDATA-138]
_ = x[OINLMARK-139] _ = x[OSPTR-139]
_ = x[OLINKSYMOFFSET-140] _ = x[OCFUNC-140]
_ = x[OJUMPTABLE-141] _ = x[OCHECKNIL-141]
_ = x[ODYNAMICDOTTYPE-142] _ = x[ORESULT-142]
_ = x[ODYNAMICDOTTYPE2-143] _ = x[OINLMARK-143]
_ = x[ODYNAMICTYPE-144] _ = x[OLINKSYMOFFSET-144]
_ = x[OTAILCALL-145] _ = x[OJUMPTABLE-145]
_ = x[OGETG-146] _ = x[ODYNAMICDOTTYPE-146]
_ = x[OGETCALLERPC-147] _ = x[ODYNAMICDOTTYPE2-147]
_ = x[OGETCALLERSP-148] _ = x[ODYNAMICTYPE-148]
_ = x[OEND-149] _ = x[OTAILCALL-149]
_ = x[OGETG-150]
_ = x[OGETCALLERPC-151]
_ = x[OGETCALLERSP-152]
_ = x[OEND-153]
} }
const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND" const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERSTRINGHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEUNSAFESLICEDATAUNSAFESTRINGUNSAFESTRINGDATAMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTINLCALLEFACEITABIDATASPTRCFUNCCHECKNILRESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
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, 132, 134, 137, 147, 154, 161, 168, 172, 176, 184, 192, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 282, 289, 293, 296, 303, 311, 318, 324, 327, 333, 340, 348, 352, 359, 367, 369, 371, 373, 375, 377, 379, 384, 389, 397, 400, 409, 412, 416, 424, 431, 440, 453, 456, 459, 462, 465, 468, 471, 477, 480, 483, 489, 493, 496, 500, 505, 510, 516, 521, 525, 530, 538, 546, 552, 561, 572, 579, 588, 592, 599, 607, 611, 615, 622, 629, 637, 643, 652, 663, 671, 680, 685, 690, 694, 702, 707, 711, 714, 718, 720, 725, 727, 732, 738, 744, 750, 756, 764, 771, 776, 780, 785, 789, 794, 802, 808, 815, 828, 837, 851, 866, 877, 885, 889, 900, 911, 914} 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, 132, 134, 137, 147, 154, 161, 168, 172, 176, 184, 192, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 282, 289, 293, 296, 303, 311, 318, 324, 327, 333, 340, 348, 352, 359, 367, 369, 371, 373, 375, 377, 379, 384, 389, 397, 400, 409, 412, 416, 424, 431, 440, 453, 456, 459, 462, 465, 468, 471, 477, 480, 483, 489, 493, 496, 500, 505, 510, 516, 521, 525, 530, 538, 546, 552, 561, 572, 584, 591, 600, 604, 611, 619, 623, 627, 634, 641, 649, 655, 664, 675, 690, 702, 718, 726, 735, 740, 745, 749, 757, 762, 766, 769, 773, 775, 780, 782, 787, 793, 799, 805, 811, 819, 826, 831, 835, 840, 844, 849, 857, 863, 870, 883, 892, 906, 921, 932, 940, 944, 955, 966, 969}
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

@ -900,7 +900,7 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
transformArgs(n) transformArgs(n)
fallthrough fallthrough
case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRINGDATA:
u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0]) u := ir.NewUnaryExpr(n.Pos(), op, n.Args[0])
u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init u1 := typed(n.Type(), ir.InitExpr(n.Init(), u)) // typecheckargs can add to old.Init
switch op { switch op {
@ -913,12 +913,12 @@ func transformBuiltin(n *ir.CallExpr) ir.Node {
case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: case ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
// This corresponds to the EvalConst() call near end of typecheck(). // This corresponds to the EvalConst() call near end of typecheck().
return typecheck.EvalConst(u1) return typecheck.EvalConst(u1)
case ir.OCLOSE, ir.ONEW: case ir.OCLOSE, ir.ONEW, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
// nothing more to do // nothing more to do
return u1 return u1
} }
case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
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))

View File

@ -1460,7 +1460,7 @@ func (s *state) stmt(n ir.Node) {
s.callResult(n, callNormal) s.callResult(n, callNormal)
if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PFUNC { if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.ONAME && n.X.(*ir.Name).Class == ir.PFUNC {
if fn := n.X.Sym().Name; base.Flag.CompilingRuntime && fn == "throw" || if fn := n.X.Sym().Name; base.Flag.CompilingRuntime && fn == "throw" ||
n.X.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap" || fn == "panicunsafeslicelen" || fn == "panicunsafeslicenilptr") { n.X.Sym().Pkg == ir.Pkgs.Runtime && (fn == "throwinit" || fn == "gopanic" || fn == "panicwrap" || fn == "block" || fn == "panicmakeslicelen" || fn == "panicmakeslicecap" || fn == "panicunsafeslicelen" || fn == "panicunsafeslicenilptr" || fn == "panicunsafestringlen" || fn == "panicunsafestringnilptr") {
m := s.mem() m := s.mem()
b := s.endBlock() b := s.endBlock()
b.Kind = ssa.BlockExit b.Kind = ssa.BlockExit
@ -3242,6 +3242,12 @@ func (s *state) exprCheckPtr(n ir.Node, checkPtrOK bool) *ssa.Value {
c := s.expr(n.Cap) c := s.expr(n.Cap)
return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c) return s.newValue3(ssa.OpSliceMake, n.Type(), p, l, c)
case ir.OSTRINGHEADER:
n := n.(*ir.StringHeaderExpr)
p := s.expr(n.Ptr)
l := s.expr(n.Len)
return s.newValue2(ssa.OpStringMake, n.Type(), p, l)
case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR: case ir.OSLICE, ir.OSLICEARR, ir.OSLICE3, ir.OSLICE3ARR:
n := n.(*ir.SliceExpr) n := n.(*ir.SliceExpr)
check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr() check := s.checkPtrEnabled && n.Op() == ir.OSLICE3ARR && n.X.Op() == ir.OCONVNOP && n.X.(*ir.ConvExpr).X.Type().IsUnsafePtr()

View File

@ -137,76 +137,79 @@ var runtimeDecls = [...]struct {
{"unsafeslicecheckptr", funcTag, 118}, {"unsafeslicecheckptr", funcTag, 118},
{"panicunsafeslicelen", funcTag, 9}, {"panicunsafeslicelen", funcTag, 9},
{"panicunsafeslicenilptr", funcTag, 9}, {"panicunsafeslicenilptr", funcTag, 9},
{"mulUintptr", funcTag, 119}, {"unsafestringcheckptr", funcTag, 119},
{"memmove", funcTag, 120}, {"panicunsafestringlen", funcTag, 9},
{"memclrNoHeapPointers", funcTag, 121}, {"panicunsafestringnilptr", funcTag, 9},
{"memclrHasPointers", funcTag, 121}, {"mulUintptr", funcTag, 120},
{"memequal", funcTag, 122}, {"memmove", funcTag, 121},
{"memequal0", funcTag, 123}, {"memclrNoHeapPointers", funcTag, 122},
{"memequal8", funcTag, 123}, {"memclrHasPointers", funcTag, 122},
{"memequal16", funcTag, 123}, {"memequal", funcTag, 123},
{"memequal32", funcTag, 123}, {"memequal0", funcTag, 124},
{"memequal64", funcTag, 123}, {"memequal8", funcTag, 124},
{"memequal128", funcTag, 123}, {"memequal16", funcTag, 124},
{"f32equal", funcTag, 124}, {"memequal32", funcTag, 124},
{"f64equal", funcTag, 124}, {"memequal64", funcTag, 124},
{"c64equal", funcTag, 124}, {"memequal128", funcTag, 124},
{"c128equal", funcTag, 124}, {"f32equal", funcTag, 125},
{"strequal", funcTag, 124}, {"f64equal", funcTag, 125},
{"interequal", funcTag, 124}, {"c64equal", funcTag, 125},
{"nilinterequal", funcTag, 124}, {"c128equal", funcTag, 125},
{"memhash", funcTag, 125}, {"strequal", funcTag, 125},
{"memhash0", funcTag, 126}, {"interequal", funcTag, 125},
{"memhash8", funcTag, 126}, {"nilinterequal", funcTag, 125},
{"memhash16", funcTag, 126}, {"memhash", funcTag, 126},
{"memhash32", funcTag, 126}, {"memhash0", funcTag, 127},
{"memhash64", funcTag, 126}, {"memhash8", funcTag, 127},
{"memhash128", funcTag, 126}, {"memhash16", funcTag, 127},
{"f32hash", funcTag, 126}, {"memhash32", funcTag, 127},
{"f64hash", funcTag, 126}, {"memhash64", funcTag, 127},
{"c64hash", funcTag, 126}, {"memhash128", funcTag, 127},
{"c128hash", funcTag, 126}, {"f32hash", funcTag, 127},
{"strhash", funcTag, 126}, {"f64hash", funcTag, 127},
{"interhash", funcTag, 126}, {"c64hash", funcTag, 127},
{"nilinterhash", funcTag, 126}, {"c128hash", funcTag, 127},
{"int64div", funcTag, 127}, {"strhash", funcTag, 127},
{"uint64div", funcTag, 128}, {"interhash", funcTag, 127},
{"int64mod", funcTag, 127}, {"nilinterhash", funcTag, 127},
{"uint64mod", funcTag, 128}, {"int64div", funcTag, 128},
{"float64toint64", funcTag, 129}, {"uint64div", funcTag, 129},
{"float64touint64", funcTag, 130}, {"int64mod", funcTag, 128},
{"float64touint32", funcTag, 131}, {"uint64mod", funcTag, 129},
{"int64tofloat64", funcTag, 132}, {"float64toint64", funcTag, 130},
{"int64tofloat32", funcTag, 134}, {"float64touint64", funcTag, 131},
{"uint64tofloat64", funcTag, 135}, {"float64touint32", funcTag, 132},
{"uint64tofloat32", funcTag, 136}, {"int64tofloat64", funcTag, 133},
{"uint32tofloat64", funcTag, 137}, {"int64tofloat32", funcTag, 135},
{"complex128div", funcTag, 138}, {"uint64tofloat64", funcTag, 136},
{"getcallerpc", funcTag, 139}, {"uint64tofloat32", funcTag, 137},
{"getcallersp", funcTag, 139}, {"uint32tofloat64", funcTag, 138},
{"complex128div", funcTag, 139},
{"getcallerpc", funcTag, 140},
{"getcallersp", funcTag, 140},
{"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, 140}, {"racereadrange", funcTag, 141},
{"racewriterange", funcTag, 140}, {"racewriterange", funcTag, 141},
{"msanread", funcTag, 140}, {"msanread", funcTag, 141},
{"msanwrite", funcTag, 140}, {"msanwrite", funcTag, 141},
{"msanmove", funcTag, 141}, {"msanmove", funcTag, 142},
{"asanread", funcTag, 140}, {"asanread", funcTag, 141},
{"asanwrite", funcTag, 140}, {"asanwrite", funcTag, 141},
{"checkptrAlignment", funcTag, 142}, {"checkptrAlignment", funcTag, 143},
{"checkptrArithmetic", funcTag, 144}, {"checkptrArithmetic", funcTag, 145},
{"libfuzzerTraceCmp1", funcTag, 145}, {"libfuzzerTraceCmp1", funcTag, 146},
{"libfuzzerTraceCmp2", funcTag, 146}, {"libfuzzerTraceCmp2", funcTag, 147},
{"libfuzzerTraceCmp4", funcTag, 147}, {"libfuzzerTraceCmp4", funcTag, 148},
{"libfuzzerTraceCmp8", funcTag, 148}, {"libfuzzerTraceCmp8", funcTag, 149},
{"libfuzzerTraceConstCmp1", funcTag, 145}, {"libfuzzerTraceConstCmp1", funcTag, 146},
{"libfuzzerTraceConstCmp2", funcTag, 146}, {"libfuzzerTraceConstCmp2", funcTag, 147},
{"libfuzzerTraceConstCmp4", funcTag, 147}, {"libfuzzerTraceConstCmp4", funcTag, 148},
{"libfuzzerTraceConstCmp8", funcTag, 148}, {"libfuzzerTraceConstCmp8", funcTag, 149},
{"libfuzzerHookStrCmp", funcTag, 149}, {"libfuzzerHookStrCmp", funcTag, 150},
{"libfuzzerHookEqualFold", funcTag, 149}, {"libfuzzerHookEqualFold", funcTag, 150},
{"x86HasPOPCNT", varTag, 6}, {"x86HasPOPCNT", varTag, 6},
{"x86HasSSE41", varTag, 6}, {"x86HasSSE41", varTag, 6},
{"x86HasFMA", varTag, 6}, {"x86HasFMA", varTag, 6},
@ -230,7 +233,7 @@ func params(tlist ...*types.Type) []*types.Field {
} }
func runtimeTypes() []*types.Type { func runtimeTypes() []*types.Type {
var typs [150]*types.Type var typs [151]*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]
@ -350,36 +353,37 @@ func runtimeTypes() []*types.Type {
typs[116] = types.NewSlice(typs[2]) typs[116] = types.NewSlice(typs[2])
typs[117] = newSig(params(typs[1], typs[116], typs[15]), params(typs[116])) typs[117] = newSig(params(typs[1], typs[116], typs[15]), params(typs[116]))
typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil) typs[118] = newSig(params(typs[1], typs[7], typs[22]), nil)
typs[119] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6])) typs[119] = newSig(params(typs[7], typs[22]), nil)
typs[120] = newSig(params(typs[3], typs[3], typs[5]), nil) typs[120] = newSig(params(typs[5], typs[5]), params(typs[5], typs[6]))
typs[121] = newSig(params(typs[7], typs[5]), nil) typs[121] = newSig(params(typs[3], typs[3], typs[5]), nil)
typs[122] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6])) typs[122] = newSig(params(typs[7], typs[5]), nil)
typs[123] = newSig(params(typs[3], typs[3]), params(typs[6])) typs[123] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
typs[124] = newSig(params(typs[7], typs[7]), params(typs[6])) typs[124] = newSig(params(typs[3], typs[3]), params(typs[6]))
typs[125] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5])) typs[125] = newSig(params(typs[7], typs[7]), params(typs[6]))
typs[126] = newSig(params(typs[7], typs[5]), params(typs[5])) typs[126] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5]))
typs[127] = newSig(params(typs[22], typs[22]), params(typs[22])) typs[127] = newSig(params(typs[7], typs[5]), params(typs[5]))
typs[128] = newSig(params(typs[24], typs[24]), params(typs[24])) typs[128] = newSig(params(typs[22], typs[22]), params(typs[22]))
typs[129] = newSig(params(typs[20]), params(typs[22])) typs[129] = newSig(params(typs[24], typs[24]), params(typs[24]))
typs[130] = newSig(params(typs[20]), params(typs[24])) typs[130] = newSig(params(typs[20]), params(typs[22]))
typs[131] = newSig(params(typs[20]), params(typs[62])) typs[131] = newSig(params(typs[20]), params(typs[24]))
typs[132] = newSig(params(typs[22]), params(typs[20])) typs[132] = newSig(params(typs[20]), params(typs[62]))
typs[133] = types.Types[types.TFLOAT32] typs[133] = newSig(params(typs[22]), params(typs[20]))
typs[134] = newSig(params(typs[22]), params(typs[133])) typs[134] = types.Types[types.TFLOAT32]
typs[135] = newSig(params(typs[24]), params(typs[20])) typs[135] = newSig(params(typs[22]), params(typs[134]))
typs[136] = newSig(params(typs[24]), params(typs[133])) typs[136] = newSig(params(typs[24]), params(typs[20]))
typs[137] = newSig(params(typs[62]), params(typs[20])) typs[137] = newSig(params(typs[24]), params(typs[134]))
typs[138] = newSig(params(typs[26], typs[26]), params(typs[26])) typs[138] = newSig(params(typs[62]), params(typs[20]))
typs[139] = newSig(nil, params(typs[5])) typs[139] = newSig(params(typs[26], typs[26]), params(typs[26]))
typs[140] = newSig(params(typs[5], typs[5]), nil) typs[140] = newSig(nil, params(typs[5]))
typs[141] = newSig(params(typs[5], typs[5], typs[5]), nil) typs[141] = newSig(params(typs[5], typs[5]), nil)
typs[142] = newSig(params(typs[7], typs[1], typs[5]), nil) typs[142] = newSig(params(typs[5], typs[5], typs[5]), nil)
typs[143] = types.NewSlice(typs[7]) typs[143] = newSig(params(typs[7], typs[1], typs[5]), nil)
typs[144] = newSig(params(typs[7], typs[143]), nil) typs[144] = types.NewSlice(typs[7])
typs[145] = newSig(params(typs[66], typs[66], typs[15]), nil) typs[145] = newSig(params(typs[7], typs[144]), nil)
typs[146] = newSig(params(typs[60], typs[60], typs[15]), nil) typs[146] = newSig(params(typs[66], typs[66], typs[15]), nil)
typs[147] = newSig(params(typs[62], typs[62], typs[15]), nil) typs[147] = newSig(params(typs[60], typs[60], typs[15]), nil)
typs[148] = newSig(params(typs[24], typs[24], typs[15]), nil) typs[148] = newSig(params(typs[62], typs[62], typs[15]), nil)
typs[149] = newSig(params(typs[28], typs[28], typs[15]), nil) typs[149] = newSig(params(typs[24], typs[24], typs[15]), nil)
typs[150] = newSig(params(typs[28], typs[28], typs[15]), nil)
return typs[:] return typs[:]
} }

View File

@ -183,6 +183,9 @@ func growslice(typ *byte, old []any, cap int) (ary []any)
func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64) func unsafeslicecheckptr(typ *byte, ptr unsafe.Pointer, len int64)
func panicunsafeslicelen() func panicunsafeslicelen()
func panicunsafeslicenilptr() func panicunsafeslicenilptr()
func unsafestringcheckptr(ptr unsafe.Pointer, len int64)
func panicunsafestringlen()
func panicunsafestringnilptr()
func mulUintptr(x, y uintptr) (uintptr, bool) func mulUintptr(x, y uintptr) (uintptr, bool)

View File

@ -758,7 +758,10 @@ func callOrChan(n ir.Node) bool {
ir.ORECOVER, ir.ORECOVER,
ir.ORECV, ir.ORECV,
ir.OUNSAFEADD, ir.OUNSAFEADD,
ir.OUNSAFESLICE: ir.OUNSAFESLICE,
ir.OUNSAFESLICEDATA,
ir.OUNSAFESTRING,
ir.OUNSAFESTRINGDATA:
return true return true
} }
return false return false

View File

@ -659,6 +659,40 @@ func tcLenCap(n *ir.UnaryExpr) ir.Node {
return n return n
} }
// tcUnsafeData typechecks an OUNSAFESLICEDATA or OUNSAFESTRINGDATA node.
func tcUnsafeData(n *ir.UnaryExpr) ir.Node {
n.X = Expr(n.X)
n.X = DefaultLit(n.X, nil)
l := n.X
t := l.Type()
if t == nil {
n.SetType(nil)
return n
}
var kind types.Kind
if n.Op() == ir.OUNSAFESLICEDATA {
kind = types.TSLICE
} else {
/* kind is string */
kind = types.TSTRING
}
if t.Kind() != kind {
base.Errorf("invalid argument %L for %v", l, n.Op())
n.SetType(nil)
return n
}
if kind == types.TSTRING {
t = types.ByteType
} else {
t = t.Elem()
}
n.SetType(types.NewPtr(t))
return n
}
// tcRecv typechecks an ORECV node. // tcRecv typechecks an ORECV node.
func tcRecv(n *ir.UnaryExpr) ir.Node { func tcRecv(n *ir.UnaryExpr) ir.Node {
n.X = Expr(n.X) n.X = Expr(n.X)
@ -812,6 +846,31 @@ func tcSliceHeader(n *ir.SliceHeaderExpr) ir.Node {
return n return n
} }
// tcStringHeader typechecks an OSTRINGHEADER node.
func tcStringHeader(n *ir.StringHeaderExpr) ir.Node {
t := n.Type()
if t == nil {
base.Fatalf("no type specified for OSTRINGHEADER")
}
if !t.IsString() {
base.Fatalf("invalid type %v for OSTRINGHEADER", n.Type())
}
if n.Ptr == nil || n.Ptr.Type() == nil || !n.Ptr.Type().IsUnsafePtr() {
base.Fatalf("need unsafe.Pointer for OSTRINGHEADER")
}
n.Ptr = Expr(n.Ptr)
n.Len = DefaultLit(Expr(n.Len), types.Types[types.TINT])
if ir.IsConst(n.Len, constant.Int) && ir.Int64Val(n.Len) < 0 {
base.Fatalf("len for OSTRINGHEADER must be non-negative")
}
return n
}
// tcStar typechecks an ODEREF node, which may be an expression or a type. // tcStar typechecks an ODEREF node, which may be an expression or a type.
func tcStar(n *ir.StarExpr, top int) ir.Node { func tcStar(n *ir.StarExpr, top int) ir.Node {
n.X = typecheck(n.X, ctxExpr|ctxType) n.X = typecheck(n.X, ctxExpr|ctxType)

View File

@ -299,7 +299,7 @@ func tcCall(n *ir.CallExpr, top int) ir.Node {
n.SetTypecheck(0) // re-typechecking new op is OK, not a loop n.SetTypecheck(0) // re-typechecking new op is OK, not a loop
return typecheck(n, top) return typecheck(n, top)
case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL: case ir.OCAP, ir.OCLOSE, ir.OIMAG, ir.OLEN, ir.OPANIC, ir.OREAL, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
typecheckargs(n) typecheckargs(n)
fallthrough fallthrough
case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF: case ir.ONEW, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
@ -311,7 +311,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, ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OCOMPLEX, ir.OCOPY, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
typecheckargs(n) typecheckargs(n)
arg1, arg2, ok := needTwoArgs(n) arg1, arg2, ok := needTwoArgs(n)
if !ok { if !ok {
@ -907,10 +907,31 @@ func tcUnsafeSlice(n *ir.BinaryExpr) *ir.BinaryExpr {
base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed") base.Errorf("unsafe.Slice of incomplete (or unallocatable) type not allowed")
} }
if !checkunsafeslice(&n.Y) { if !checkunsafesliceorstring(n.Op(), &n.Y) {
n.SetType(nil) n.SetType(nil)
return n return n
} }
n.SetType(types.NewSlice(t.Elem())) n.SetType(types.NewSlice(t.Elem()))
return n return n
} }
// tcUnsafeString typechecks an OUNSAFESTRING node.
func tcUnsafeString(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() || !types.Identical(t.Elem(), types.Types[types.TUINT8]) {
base.Errorf("first argument to unsafe.String must be *byte; have %L", t)
}
if !checkunsafesliceorstring(n.Op(), &n.Y) {
n.SetType(nil)
return n
}
n.SetType(types.Types[types.TSTRING])
return n
}

View File

@ -1964,7 +1964,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.Max) w.expr(n.Max)
w.typ(n.Type()) w.typ(n.Type())
case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
// 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())
@ -1982,7 +1982,7 @@ func (w *exportWriter) expr(n ir.Node) {
w.expr(n.X) w.expr(n.X)
w.bool(n.Implicit()) w.bool(n.Implicit())
case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
n := n.(*ir.UnaryExpr) n := n.(*ir.UnaryExpr)
w.op(n.Op()) w.op(n.Op())
w.pos(n.Pos()) w.pos(n.Pos())

View File

@ -1494,16 +1494,18 @@ func (r *importReader) node() ir.Node {
n.SetImplicit(r.bool()) n.SetImplicit(r.bool())
return n return n
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: 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, ir.OUNSAFESLICEDATA, ir.OUNSAFESTRING, ir.OUNSAFESTRINGDATA:
pos := r.pos() pos := r.pos()
switch op { switch op {
case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE: case ir.OCOPY, ir.OCOMPLEX, ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
init := r.stmtList() init := r.stmtList()
n := ir.NewBinaryExpr(pos, op, r.expr(), r.expr()) n := ir.NewBinaryExpr(pos, op, r.expr(), r.expr())
n.SetInit(init) n.SetInit(init)
n.SetType(r.typ()) n.SetType(r.typ())
return n return n
case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC: case ir.OREAL, ir.OIMAG, ir.OCAP, ir.OCLOSE, ir.OLEN, ir.ONEW, ir.OPANIC, ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
n := ir.NewUnaryExpr(pos, op, r.expr()) n := ir.NewUnaryExpr(pos, op, r.expr())
if op != ir.OPANIC { if op != ir.OPANIC {
n.SetType(r.typ()) n.SetType(r.typ())

View File

@ -610,6 +610,10 @@ func typecheck1(n ir.Node, top int) ir.Node {
n := n.(*ir.SliceHeaderExpr) n := n.(*ir.SliceHeaderExpr)
return tcSliceHeader(n) return tcSliceHeader(n)
case ir.OSTRINGHEADER:
n := n.(*ir.StringHeaderExpr)
return tcStringHeader(n)
case ir.OMAKESLICECOPY: case ir.OMAKESLICECOPY:
n := n.(*ir.MakeExpr) n := n.(*ir.MakeExpr)
return tcMakeSliceCopy(n) return tcMakeSliceCopy(n)
@ -692,6 +696,18 @@ func typecheck1(n ir.Node, top int) ir.Node {
n := n.(*ir.BinaryExpr) n := n.(*ir.BinaryExpr)
return tcUnsafeSlice(n) return tcUnsafeSlice(n)
case ir.OUNSAFESLICEDATA:
n := n.(*ir.UnaryExpr)
return tcUnsafeData(n)
case ir.OUNSAFESTRING:
n := n.(*ir.BinaryExpr)
return tcUnsafeString(n)
case ir.OUNSAFESTRINGDATA:
n := n.(*ir.UnaryExpr)
return tcUnsafeData(n)
case ir.OCLOSURE: case ir.OCLOSURE:
n := n.(*ir.ClosureExpr) n := n.(*ir.ClosureExpr)
return tcClosure(n, top) return tcClosure(n, top)
@ -1647,11 +1663,11 @@ func checkmake(t *types.Type, arg string, np *ir.Node) bool {
return true return true
} }
// checkunsafeslice is like checkmake but for unsafe.Slice. // checkunsafesliceorstring is like checkmake but for unsafe.{Slice,String}.
func checkunsafeslice(np *ir.Node) bool { func checkunsafesliceorstring(op ir.Op, np *ir.Node) bool {
n := *np n := *np
if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL { if !n.Type().IsInteger() && n.Type().Kind() != types.TIDEAL {
base.Errorf("non-integer len argument in unsafe.Slice - %v", n.Type()) base.Errorf("non-integer len argument in %v - %v", op, n.Type())
return false return false
} }
@ -1660,11 +1676,11 @@ func checkunsafeslice(np *ir.Node) bool {
if n.Op() == ir.OLITERAL { if n.Op() == ir.OLITERAL {
v := toint(n.Val()) v := toint(n.Val())
if constant.Sign(v) < 0 { if constant.Sign(v) < 0 {
base.Errorf("negative len argument in unsafe.Slice") base.Errorf("negative len argument in %v", op)
return false return false
} }
if ir.ConstOverflow(v, types.Types[types.TINT]) { if ir.ConstOverflow(v, types.Types[types.TINT]) {
base.Errorf("len argument too large in unsafe.Slice") base.Errorf("len argument too large in %v", op)
return false return false
} }
} }

View File

@ -58,6 +58,9 @@ var unsafeFuncs = [...]struct {
{"Offsetof", ir.OOFFSETOF}, {"Offsetof", ir.OOFFSETOF},
{"Sizeof", ir.OSIZEOF}, {"Sizeof", ir.OSIZEOF},
{"Slice", ir.OUNSAFESLICE}, {"Slice", ir.OUNSAFESLICE},
{"SliceData", ir.OUNSAFESLICEDATA},
{"String", ir.OUNSAFESTRING},
{"StringData", ir.OUNSAFESTRINGDATA},
} }
// InitUniverse initializes the universe block. // InitUniverse initializes the universe block.

View File

@ -642,6 +642,14 @@ func walkRecoverFP(nn *ir.CallExpr, init *ir.Nodes) ir.Node {
return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init)) return mkcall("gorecover", nn.Type(), init, walkExpr(nn.Args[0], init))
} }
// walkUnsafeData walks an OUNSAFESLICEDATA or OUNSAFESTRINGDATA expression.
func walkUnsafeData(n *ir.UnaryExpr, init *ir.Nodes) ir.Node {
slice := walkExpr(n.X, init)
res := typecheck.Expr(ir.NewUnaryExpr(n.Pos(), ir.OSPTR, slice))
res.SetType(n.Type())
return walkExpr(res, init)
}
func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
ptr := safeExpr(n.X, init) ptr := safeExpr(n.X, init)
len := safeExpr(n.Y, init) len := safeExpr(n.Y, init)
@ -731,6 +739,64 @@ func walkUnsafeSlice(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
return walkExpr(typecheck.Expr(h), init) return walkExpr(typecheck.Expr(h), init)
} }
func walkUnsafeString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
ptr := safeExpr(n.X, init)
len := safeExpr(n.Y, init)
lenType := types.Types[types.TINT64]
unsafePtr := typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR])
// If checkptr enabled, call runtime.unsafestringcheckptr to check ptr and len.
// for simplicity, unsafestringcheckptr always uses int64.
// Type checking guarantees that TIDEAL len are positive and fit in an int.
if ir.ShouldCheckPtr(ir.CurFunc, 1) {
fnname := "unsafestringcheckptr"
fn := typecheck.LookupRuntime(fnname)
init.Append(mkcall1(fn, nil, init, unsafePtr, typecheck.Conv(len, lenType)))
} else {
// Otherwise, open code unsafe.String to prevent runtime call overhead.
// Keep this code in sync with runtime.unsafestring{,64}
if len.Type().IsKind(types.TIDEAL) || len.Type().Size() <= types.Types[types.TUINT].Size() {
lenType = types.Types[types.TINT]
} else {
// len64 := int64(len)
// if int64(int(len64)) != len64 {
// panicunsafestringlen()
// }
len64 := typecheck.Conv(len, lenType)
nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
nif.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, typecheck.Conv(typecheck.Conv(len64, types.Types[types.TINT]), lenType), len64)
nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body))
appendWalkStmt(init, nif)
}
// if len < 0 { panicunsafestringlen() }
nif := ir.NewIfStmt(base.Pos, nil, nil, nil)
nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, typecheck.Conv(len, lenType), ir.NewInt(0))
nif.Body.Append(mkcall("panicunsafestringlen", nil, &nif.Body))
appendWalkStmt(init, nif)
// if uintpr(len) > -uintptr(ptr) {
// if ptr == nil {
// panicunsafestringnilptr()
// }
// panicunsafeslicelen()
// }
nifLen := ir.NewIfStmt(base.Pos, nil, nil, nil)
nifLen.Cond = ir.NewBinaryExpr(base.Pos, ir.OGT, typecheck.Conv(len, types.Types[types.TUINTPTR]), ir.NewUnaryExpr(base.Pos, ir.ONEG, typecheck.Conv(unsafePtr, types.Types[types.TUINTPTR])))
nifPtr := ir.NewIfStmt(base.Pos, nil, nil, nil)
nifPtr.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, unsafePtr, typecheck.NodNil())
nifPtr.Body.Append(mkcall("panicunsafestringnilptr", nil, &nifPtr.Body))
nifLen.Body.Append(nifPtr, mkcall("panicunsafestringlen", nil, &nifLen.Body))
appendWalkStmt(init, nifLen)
}
h := ir.NewStringHeaderExpr(n.Pos(),
typecheck.Conv(ptr, types.Types[types.TUNSAFEPTR]),
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

@ -129,6 +129,14 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.BinaryExpr) n := n.(*ir.BinaryExpr)
return walkUnsafeSlice(n, init) return walkUnsafeSlice(n, init)
case ir.OUNSAFESTRING:
n := n.(*ir.BinaryExpr)
return walkUnsafeString(n, init)
case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
n := n.(*ir.UnaryExpr)
return walkUnsafeData(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)
@ -244,6 +252,10 @@ func walkExpr1(n ir.Node, init *ir.Nodes) ir.Node {
n := n.(*ir.SliceHeaderExpr) n := n.(*ir.SliceHeaderExpr)
return walkSliceHeader(n, init) return walkSliceHeader(n, init)
case ir.OSTRINGHEADER:
n := n.(*ir.StringHeaderExpr)
return walkStringHeader(n, init)
case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR: case ir.OSLICE, ir.OSLICEARR, ir.OSLICESTR, ir.OSLICE3, ir.OSLICE3ARR:
n := n.(*ir.SliceExpr) n := n.(*ir.SliceExpr)
return walkSlice(n, init) return walkSlice(n, init)
@ -849,6 +861,13 @@ func walkSliceHeader(n *ir.SliceHeaderExpr, init *ir.Nodes) ir.Node {
return n return n
} }
// walkStringHeader walks an OSTRINGHEADER node.
func walkStringHeader(n *ir.StringHeaderExpr, init *ir.Nodes) ir.Node {
n.Ptr = walkExpr(n.Ptr, init)
n.Len = walkExpr(n.Len, init)
return n
}
// TODO(josharian): combine this with its caller and simplify // TODO(josharian): combine this with its caller and simplify
func reduceSlice(n *ir.SliceExpr) ir.Node { func reduceSlice(n *ir.SliceExpr) ir.Node {
if n.High != nil && n.High.Op() == ir.OLEN && ir.SameSafeExpr(n.X, n.High.(*ir.UnaryExpr).X) { if n.High != nil && n.High.Op() == ir.OLEN && ir.SameSafeExpr(n.X, n.High.(*ir.UnaryExpr).X) {

View File

@ -342,7 +342,7 @@ func mayCall(n ir.Node) bool {
ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL, ir.OCAP, ir.OIMAG, ir.OLEN, ir.OREAL,
ir.OCONVNOP, ir.ODOT, ir.OCONVNOP, ir.ODOT,
ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR, ir.OCFUNC, ir.OIDATA, ir.OITAB, ir.OSPTR,
ir.OBYTES2STRTMP, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OSLICEHEADER: ir.OBYTES2STRTMP, ir.OGETG, ir.OGETCALLERPC, ir.OGETCALLERSP, ir.OSLICEHEADER, ir.OSTRINGHEADER:
// ok: operations that don't require function calls. // ok: operations that don't require function calls.
// Expand as needed. // Expand as needed.
} }

View File

@ -39,6 +39,8 @@ func TestCheckPtr(t *testing.T) {
{"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"}, {"CheckPtrSmall", "fatal error: checkptr: pointer arithmetic computed bad pointer value\n"},
{"CheckPtrSliceOK", ""}, {"CheckPtrSliceOK", ""},
{"CheckPtrSliceFail", "fatal error: checkptr: unsafe.Slice result straddles multiple allocations\n"}, {"CheckPtrSliceFail", "fatal error: checkptr: unsafe.Slice result straddles multiple allocations\n"},
{"CheckPtrStringOK", ""},
{"CheckPtrStringFail", "fatal error: checkptr: unsafe.String result straddles multiple allocations\n"},
} }
for _, tc := range testCases { for _, tc := range testCases {

View File

@ -123,54 +123,6 @@ func mulUintptr(a, b uintptr) (uintptr, bool) {
return math.MulUintptr(a, b) return math.MulUintptr(a, b)
} }
// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice
func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
if len < 0 {
panicunsafeslicelen()
}
if et.size == 0 {
if ptr == nil && len > 0 {
panicunsafeslicenilptr()
}
}
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > -uintptr(ptr) {
if ptr == nil {
panicunsafeslicenilptr()
}
panicunsafeslicelen()
}
}
// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice
func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
len := int(len64)
if int64(len) != len64 {
panicunsafeslicelen()
}
unsafeslice(et, ptr, len)
}
func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) {
unsafeslice64(et, ptr, len64)
// Check that underlying array doesn't straddle multiple heap objects.
// unsafeslice64 has already checked for overflow.
if checkptrStraddles(ptr, uintptr(len64)*et.size) {
throw("checkptr: unsafe.Slice result straddles multiple allocations")
}
}
func panicunsafeslicelen() {
panic(errorString("unsafe.Slice: len out of range"))
}
func panicunsafeslicenilptr() {
panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
}
// 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

View File

@ -20,6 +20,8 @@ func init() {
register("CheckPtrSmall", CheckPtrSmall) register("CheckPtrSmall", CheckPtrSmall)
register("CheckPtrSliceOK", CheckPtrSliceOK) register("CheckPtrSliceOK", CheckPtrSliceOK)
register("CheckPtrSliceFail", CheckPtrSliceFail) register("CheckPtrSliceFail", CheckPtrSliceFail)
register("CheckPtrStringOK", CheckPtrStringOK)
register("CheckPtrStringFail", CheckPtrStringFail)
register("CheckPtrAlignmentNested", CheckPtrAlignmentNested) register("CheckPtrAlignmentNested", CheckPtrAlignmentNested)
} }
@ -98,6 +100,17 @@ func CheckPtrSliceFail() {
sink2 = unsafe.Slice(p, 100) sink2 = unsafe.Slice(p, 100)
} }
func CheckPtrStringOK() {
p := new([4]byte)
sink2 = unsafe.String(&p[1], 3)
}
func CheckPtrStringFail() {
p := new(byte)
sink2 = p
sink2 = unsafe.String(p, 100)
}
func CheckPtrAlignmentNested() { func CheckPtrAlignmentNested() {
s := make([]int8, 100) s := make([]int8, 100)
p := unsafe.Pointer(&s[0]) p := unsafe.Pointer(&s[0])

98
src/runtime/unsafe.go Normal file
View File

@ -0,0 +1,98 @@
// Copyright 2022 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 (
"runtime/internal/math"
"unsafe"
)
func unsafestring(ptr unsafe.Pointer, len int) {
if len < 0 {
panicunsafestringlen()
}
if uintptr(len) > -uintptr(ptr) {
if ptr == nil {
panicunsafestringnilptr()
}
panicunsafestringlen()
}
}
// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeString
func unsafestring64(ptr unsafe.Pointer, len64 int64) {
len := int(len64)
if int64(len) != len64 {
panicunsafestringlen()
}
unsafestring(ptr, len)
}
func unsafestringcheckptr(ptr unsafe.Pointer, len64 int64) {
unsafestring64(ptr, len64)
// Check that underlying array doesn't straddle multiple heap objects.
// unsafestring64 has already checked for overflow.
if checkptrStraddles(ptr, uintptr(len64)) {
throw("checkptr: unsafe.String result straddles multiple allocations")
}
}
func panicunsafestringlen() {
panic(errorString("unsafe.String: len out of range"))
}
func panicunsafestringnilptr() {
panic(errorString("unsafe.String: ptr is nil and len is not zero"))
}
// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice
func unsafeslice(et *_type, ptr unsafe.Pointer, len int) {
if len < 0 {
panicunsafeslicelen()
}
if et.size == 0 {
if ptr == nil && len > 0 {
panicunsafeslicenilptr()
}
}
mem, overflow := math.MulUintptr(et.size, uintptr(len))
if overflow || mem > -uintptr(ptr) {
if ptr == nil {
panicunsafeslicenilptr()
}
panicunsafeslicelen()
}
}
// Keep this code in sync with cmd/compile/internal/walk/builtin.go:walkUnsafeSlice
func unsafeslice64(et *_type, ptr unsafe.Pointer, len64 int64) {
len := int(len64)
if int64(len) != len64 {
panicunsafeslicelen()
}
unsafeslice(et, ptr, len)
}
func unsafeslicecheckptr(et *_type, ptr unsafe.Pointer, len64 int64) {
unsafeslice64(et, ptr, len64)
// Check that underlying array doesn't straddle multiple heap objects.
// unsafeslice64 has already checked for overflow.
if checkptrStraddles(ptr, uintptr(len64)*et.size) {
throw("checkptr: unsafe.Slice result straddles multiple allocations")
}
}
func panicunsafeslicelen() {
panic(errorString("unsafe.Slice: len out of range"))
}
func panicunsafeslicenilptr() {
panic(errorString("unsafe.Slice: ptr is nil and len is not zero"))
}

22
test/unsafe_slice_data.go Normal file
View File

@ -0,0 +1,22 @@
// run
// Copyright 2022 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 (
"fmt"
"reflect"
"unsafe"
)
func main() {
var s = []byte("abc")
sh1 := *(*reflect.SliceHeader)(unsafe.Pointer(&s))
ptr2 := unsafe.Pointer(unsafe.SliceData(s))
if ptr2 != unsafe.Pointer(sh1.Data) {
panic(fmt.Errorf("unsafe.SliceData %p != %p", ptr2, unsafe.Pointer(sh1.Data)))
}
}

18
test/unsafe_string.go Normal file
View File

@ -0,0 +1,18 @@
// run
// Copyright 2022 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 (
"unsafe"
)
func main() {
hello := [5]byte{'m', 'o', 's', 'h', 'i'}
if unsafe.String(&hello[0], uint64(len(hello))) != "moshi" {
panic("unsafe.String convert error")
}
}

View File

@ -0,0 +1,22 @@
// run
// Copyright 2022 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 (
"fmt"
"reflect"
"unsafe"
)
func main() {
var s = "abc"
sh1 := (*reflect.StringHeader)(unsafe.Pointer(&s))
ptr2 := unsafe.Pointer(unsafe.StringData(s))
if ptr2 != unsafe.Pointer(sh1.Data) {
panic(fmt.Errorf("unsafe.StringData ret %p != %p", ptr2, unsafe.Pointer(sh1.Data)))
}
}

View File

@ -53,6 +53,44 @@ func main() {
_ = unsafe.Slice(last, 1) _ = unsafe.Slice(last, 1)
mustPanic(func() { _ = unsafe.Slice(last, 2) }) mustPanic(func() { _ = unsafe.Slice(last, 2) })
} }
// unsafe.String
{
s := unsafe.String(&p[0], len(p))
assert(s == string(p[:]))
assert(len(s) == len(p))
// the empty string
assert(unsafe.String(nil, 0) == "")
// nil pointer with positive length panics
mustPanic(func() { _ = unsafe.String(nil, 1) })
// negative length
var neg int = -1
mustPanic(func() { _ = unsafe.String(new(byte), neg) })
// length too large
var tooBig uint64 = math.MaxUint64
mustPanic(func() { _ = unsafe.String(new(byte), tooBig) })
// string memory overflows address space
last := (*byte)(unsafe.Pointer(^uintptr(0)))
_ = unsafe.String(last, 1)
mustPanic(func() { _ = unsafe.String(last, 2) })
}
// unsafe.StringData
{
var s = "string"
assert(string(unsafe.Slice(unsafe.StringData(s), len(s))) == s)
}
//unsafe.SliceData
{
var s = []byte("slice")
assert(unsafe.String(unsafe.SliceData(s), len(s)) == string(s))
}
} }
func assert(ok bool) { func assert(ok bool) {