Commit 3f2cb493 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: handle unsafe builtins like universal builtins

Reuse the same mechanisms for handling universal builtins like len to
handle unsafe.Sizeof, etc. Allows us to drop package unsafe's export
data, and simplifies some code.

Updates #17508.

Change-Id: I620e0617c24e57e8a2d7cccd0e2de34608779656
Reviewed-on: https://go-review.googlesource.com/31433
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 7eed848a
...@@ -103,7 +103,3 @@ const runtimeimport = "" + ...@@ -103,7 +103,3 @@ const runtimeimport = "" +
"r·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterange\x00\x04\x16\x90\x03\x00" + "r·1\x00^\x16\rsize·2\x00^\x00\t\x1bracewriterange\x00\x04\x16\x90\x03\x00" +
"^\x16\x92\x03\x00^\x00\t\x0fmsanread\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\t\x11msanwrit" + "^\x16\x92\x03\x00^\x00\t\x0fmsanread\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\t\x11msanwrit" +
"e\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n" "e\x00\x04\x16\x90\x03\x00^\x16\x92\x03\x00^\x00\v\xf6\x01\v\x00\x01\x00\n$$\n"
const unsafeimport = "" +
"version 2\n\n\x00\x00\x01\vunsafe\x00\t\x0fOffsetof\x00\x01:\x00\x01\x16\x00\t" +
"\vSizeof\x00\x01:\x00\x01\x16\x00\t\rAlignof\x00\x01:\x00\x01\x16\x00\v\x06\v\x00\x01\x00\n$$\n"
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// NOTE: If you change this file you must run "go generate"
// to update builtin.go. This is not done automatically
// to avoid depending on having a working compiler binary.
// +build ignore
package unsafe
// Type Pointer is constructed directly in typeinit.
// return types here are ignored; see unsafe.go
func Offsetof(any) uintptr
func Sizeof(any) uintptr
func Alignof(any) uintptr
...@@ -1657,20 +1657,9 @@ func isgoconst(n *Node) bool { ...@@ -1657,20 +1657,9 @@ func isgoconst(n *Node) bool {
return true return true
} }
// Only constant calls are unsafe.Alignof, Offsetof, and Sizeof. case OALIGNOF, OOFFSETOF, OSIZEOF:
case OCALL:
l := n.Left
for l.Op == OPAREN {
l = l.Left
}
if l.Op != ONAME || l.Sym.Pkg != unsafepkg {
break
}
if l.Sym.Name == "Alignof" || l.Sym.Name == "Offsetof" || l.Sym.Name == "Sizeof" {
return true return true
} }
}
//dump("nonconst", n); //dump("nonconst", n);
return false return false
......
...@@ -148,6 +148,7 @@ var goopnames = []string{ ...@@ -148,6 +148,7 @@ var goopnames = []string{
OADDR: "&", OADDR: "&",
OADD: "+", OADD: "+",
OADDSTR: "+", OADDSTR: "+",
OALIGNOF: "unsafe.Alignof",
OANDAND: "&&", OANDAND: "&&",
OANDNOT: "&^", OANDNOT: "&^",
OAND: "&", OAND: "&",
...@@ -188,6 +189,7 @@ var goopnames = []string{ ...@@ -188,6 +189,7 @@ var goopnames = []string{
ONEW: "new", ONEW: "new",
ONE: "!=", ONE: "!=",
ONOT: "!", ONOT: "!",
OOFFSETOF: "unsafe.Offsetof",
OOROR: "||", OOROR: "||",
OOR: "|", OOR: "|",
OPANIC: "panic", OPANIC: "panic",
...@@ -202,6 +204,7 @@ var goopnames = []string{ ...@@ -202,6 +204,7 @@ var goopnames = []string{
ORSH: ">>", ORSH: ">>",
OSELECT: "select", OSELECT: "select",
OSEND: "<-", OSEND: "<-",
OSIZEOF: "unsafe.Sizeof",
OSUB: "-", OSUB: "-",
OSWITCH: "switch", OSWITCH: "switch",
OXOR: "^", OXOR: "^",
...@@ -991,6 +994,7 @@ func (n *Node) stmtfmt(s fmt.State) { ...@@ -991,6 +994,7 @@ func (n *Node) stmtfmt(s fmt.State) {
} }
var opprec = []int{ var opprec = []int{
OALIGNOF: 8,
OAPPEND: 8, OAPPEND: 8,
OARRAYBYTESTR: 8, OARRAYBYTESTR: 8,
OARRAYLIT: 8, OARRAYLIT: 8,
...@@ -1016,12 +1020,14 @@ var opprec = []int{ ...@@ -1016,12 +1020,14 @@ var opprec = []int{
ONAME: 8, ONAME: 8,
ONEW: 8, ONEW: 8,
ONONAME: 8, ONONAME: 8,
OOFFSETOF: 8,
OPACK: 8, OPACK: 8,
OPANIC: 8, OPANIC: 8,
OPAREN: 8, OPAREN: 8,
OPRINTN: 8, OPRINTN: 8,
OPRINT: 8, OPRINT: 8,
ORUNESTR: 8, ORUNESTR: 8,
OSIZEOF: 8,
OSTRARRAYBYTE: 8, OSTRARRAYBYTE: 8,
OSTRARRAYRUNE: 8, OSTRARRAYRUNE: 8,
OSTRUCTLIT: 8, OSTRUCTLIT: 8,
...@@ -1354,6 +1360,9 @@ func (n *Node) exprfmt(s fmt.State, prec int) { ...@@ -1354,6 +1360,9 @@ func (n *Node) exprfmt(s fmt.State, prec int) {
ONEW, ONEW,
OPANIC, OPANIC,
ORECOVER, ORECOVER,
OALIGNOF,
OOFFSETOF,
OSIZEOF,
OPRINT, OPRINT,
OPRINTN: OPRINTN:
if n.Left != nil { if n.Left != nil {
......
...@@ -686,8 +686,6 @@ func loadsys() { ...@@ -686,8 +686,6 @@ func loadsys() {
importpkg = Runtimepkg importpkg = Runtimepkg
Import(bufio.NewReader(strings.NewReader(runtimeimport))) Import(bufio.NewReader(strings.NewReader(runtimeimport)))
importpkg = unsafepkg
Import(bufio.NewReader(strings.NewReader(unsafeimport)))
importpkg = nil importpkg = nil
} }
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
// +build ignore // +build ignore
// Generate builtin.go from builtin/runtime.go and builtin/unsafe.go. // Generate builtin.go from builtin/runtime.go.
// Run this after changing builtin/runtime.go and builtin/unsafe.go // Run this after changing builtin/runtime.go
// or after changing the export metadata format in the compiler. // or after changing the export metadata format in the compiler.
// Either way, you need to have a working compiler binary first. // Either way, you need to have a working compiler binary first.
// See bexport.go for how to make an export metadata format change. // See bexport.go for how to make an export metadata format change.
...@@ -33,7 +33,6 @@ func main() { ...@@ -33,7 +33,6 @@ func main() {
fmt.Fprintln(&b, "package gc") fmt.Fprintln(&b, "package gc")
mkbuiltin(&b, "runtime") mkbuiltin(&b, "runtime")
mkbuiltin(&b, "unsafe")
var err error var err error
if *stdout { if *stdout {
......
...@@ -427,6 +427,9 @@ const ( ...@@ -427,6 +427,9 @@ const (
OREAL // real(Left) OREAL // real(Left)
OIMAG // imag(Left) OIMAG // imag(Left)
OCOMPLEX // complex(Left, Right) OCOMPLEX // complex(Left, Right)
OALIGNOF // unsafe.Alignof(Left)
OOFFSETOF // unsafe.Offsetof(Left)
OSIZEOF // unsafe.Sizeof(Left)
// statements // statements
OBLOCK // { List } (block of code) OBLOCK // { List } (block of code)
......
...@@ -313,12 +313,6 @@ OpSwitch: ...@@ -313,12 +313,6 @@ OpSwitch:
n.Used = true n.Used = true
} }
if top&Ecall == 0 && isunsafebuiltin(n) {
yyerror("%v is not an expression, must be called", n)
n.Type = nil
return n
}
ok |= Erv ok |= Erv
break OpSwitch break OpSwitch
...@@ -1190,17 +1184,7 @@ OpSwitch: ...@@ -1190,17 +1184,7 @@ OpSwitch:
n.Diag |= n.Left.Diag n.Diag |= n.Left.Diag
l := n.Left l := n.Left
if l.Op == ONAME { if l.Op == ONAME && l.Etype != 0 {
if r := unsafenmagic(n); r != nil {
if n.Isddd {
yyerror("invalid use of ... with builtin %v", l)
}
n = r
n = typecheck1(n, top)
return n
}
if l.Etype != 0 {
// TODO(marvin): Fix Node.EType type union. // TODO(marvin): Fix Node.EType type union.
if n.Isddd && Op(l.Etype) != OAPPEND { if n.Isddd && Op(l.Etype) != OAPPEND {
yyerror("invalid use of ... with builtin %v", l) yyerror("invalid use of ... with builtin %v", l)
...@@ -1209,13 +1193,11 @@ OpSwitch: ...@@ -1209,13 +1193,11 @@ OpSwitch:
// builtin: OLEN, OCAP, etc. // builtin: OLEN, OCAP, etc.
// TODO(marvin): Fix Node.EType type union. // TODO(marvin): Fix Node.EType type union.
n.Op = Op(l.Etype) n.Op = Op(l.Etype)
n.Left = n.Right n.Left = n.Right
n.Right = nil n.Right = nil
n = typecheck1(n, top) n = typecheck1(n, top)
return n return n
} }
}
n.Left = defaultlit(n.Left, nil) n.Left = defaultlit(n.Left, nil)
l = n.Left l = n.Left
...@@ -1313,6 +1295,21 @@ OpSwitch: ...@@ -1313,6 +1295,21 @@ OpSwitch:
break OpSwitch break OpSwitch
case OALIGNOF, OOFFSETOF, OSIZEOF:
ok |= Erv
if !onearg(n, "%v", n.Op) {
n.Type = nil
return n
}
// any side effects disappear; ignore init
var r Node
Nodconst(&r, Types[TUINTPTR], evalunsafe(n))
r.Orig = n
n = &r
break OpSwitch
case OCAP, OLEN, OREAL, OIMAG: case OCAP, OLEN, OREAL, OIMAG:
ok |= Erv ok |= Erv
if !onearg(n, "%v", n.Op) { if !onearg(n, "%v", n.Op) {
......
...@@ -63,6 +63,15 @@ var builtinFuncs = [...]struct { ...@@ -63,6 +63,15 @@ var builtinFuncs = [...]struct {
{"recover", ORECOVER}, {"recover", ORECOVER},
} }
var unsafeFuncs = [...]struct {
name string
op Op
}{
{"Alignof", OALIGNOF},
{"Offsetof", OOFFSETOF},
{"Sizeof", OSIZEOF},
}
// initUniverse initializes the universe block. // initUniverse initializes the universe block.
func initUniverse() { func initUniverse() {
lexinit() lexinit()
...@@ -99,6 +108,13 @@ func lexinit() { ...@@ -99,6 +108,13 @@ func lexinit() {
s2.Def.Etype = EType(s.op) s2.Def.Etype = EType(s.op)
} }
for _, s := range unsafeFuncs {
s2 := Pkglookup(s.name, unsafepkg)
s2.Def = nod(ONAME, nil, nil)
s2.Def.Sym = s2
s2.Def.Etype = EType(s.op)
}
idealstring = typ(TSTRING) idealstring = typ(TSTRING)
idealbool = typ(TBOOL) idealbool = typ(TBOOL)
......
...@@ -4,126 +4,71 @@ ...@@ -4,126 +4,71 @@
package gc package gc
// unsafenmagic rewrites calls to package unsafe's functions into constants. // evalunsafe evaluates a package unsafe operation and returns the result.
func unsafenmagic(nn *Node) *Node { func evalunsafe(n *Node) int64 {
fn := nn.Left switch n.Op {
args := nn.List case OALIGNOF, OSIZEOF:
n.Left = typecheck(n.Left, Erv)
if safemode || fn == nil || fn.Op != ONAME { n.Left = defaultlit(n.Left, nil)
return nil tr := n.Left.Type
}
s := fn.Sym
if s == nil {
return nil
}
if s.Pkg != unsafepkg {
return nil
}
if args.Len() == 0 {
yyerror("missing argument for %v", s)
return nil
}
r := args.First()
var v int64
switch s.Name {
case "Alignof", "Sizeof":
r = typecheck(r, Erv)
r = defaultlit(r, nil)
tr := r.Type
if tr == nil { if tr == nil {
goto bad yyerror("invalid expression %v", n)
return 0
} }
dowidth(tr) dowidth(tr)
if s.Name == "Alignof" { if n.Op == OALIGNOF {
v = int64(tr.Align) return int64(tr.Align)
} else {
v = tr.Width
} }
return tr.Width
case "Offsetof": case OOFFSETOF:
// must be a selector. // must be a selector.
if r.Op != OXDOT { if n.Left.Op != OXDOT {
goto bad yyerror("invalid expression %v", n)
return 0
} }
// Remember base of selector to find it back after dot insertion. // Remember base of selector to find it back after dot insertion.
// Since r->left may be mutated by typechecking, check it explicitly // Since r->left may be mutated by typechecking, check it explicitly
// first to track it correctly. // first to track it correctly.
r.Left = typecheck(r.Left, Erv) n.Left.Left = typecheck(n.Left.Left, Erv)
base := r.Left base := n.Left.Left
r = typecheck(r, Erv) n.Left = typecheck(n.Left, Erv)
switch r.Op { switch n.Left.Op {
case ODOT, ODOTPTR: case ODOT, ODOTPTR:
break break
case OCALLPART: case OCALLPART:
yyerror("invalid expression %v: argument is a method value", nn) yyerror("invalid expression %v: argument is a method value", n)
goto ret return 0
default: default:
goto bad yyerror("invalid expression %v", n)
return 0
} }
// Sum offsets for dots until we reach base. // Sum offsets for dots until we reach base.
for r1 := r; r1 != base; r1 = r1.Left { var v int64
switch r1.Op { for r := n.Left; r != base; r = r.Left {
switch r.Op {
case ODOTPTR: case ODOTPTR:
// For Offsetof(s.f), s may itself be a pointer, // For Offsetof(s.f), s may itself be a pointer,
// but accessing f must not otherwise involve // but accessing f must not otherwise involve
// indirection via embedded pointer types. // indirection via embedded pointer types.
if r1.Left != base { if r.Left != base {
yyerror("invalid expression %v: selector implies indirection of embedded %v", nn, r1.Left) yyerror("invalid expression %v: selector implies indirection of embedded %v", n, r.Left)
goto ret return 0
} }
fallthrough fallthrough
case ODOT: case ODOT:
v += r1.Xoffset v += r.Xoffset
default: default:
Dump("unsafenmagic", r) Dump("unsafenmagic", n.Left)
Fatalf("impossible %#v node after dot insertion", r1.Op) Fatalf("impossible %#v node after dot insertion", r.Op)
goto bad
} }
} }
return v
default:
return nil
}
if args.Len() > 1 {
yyerror("extra arguments for %v", s)
} }
goto ret
bad:
yyerror("invalid expression %v", nn)
ret: Fatalf("unexpected op %v", n.Op)
// any side effects disappear; ignore init return 0
var val Val
val.U = new(Mpint)
val.U.(*Mpint).SetInt64(v)
n := nod(OLITERAL, nil, nil)
n.Orig = nn
n.SetVal(val)
n.Type = Types[TUINTPTR]
nn.Type = Types[TUINTPTR]
return n
}
func isunsafebuiltin(n *Node) bool {
if n == nil || n.Op != ONAME || n.Sym == nil || n.Sym.Pkg != unsafepkg {
return false
}
if n.Sym.Name == "Sizeof" {
return true
}
if n.Sym.Name == "Offsetof" {
return true
}
if n.Sym.Name == "Alignof" {
return true
}
return false
} }
...@@ -7,5 +7,4 @@ ...@@ -7,5 +7,4 @@
// issue 1951 // issue 1951
package foo package foo
import "unsafe" import "unsafe"
var v = unsafe.Sizeof // ERROR "must be called" var v = unsafe.Sizeof // ERROR "not in function call|must be called"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment