| // 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. |
| |
| package gc |
| |
| import "cmd/internal/obj" |
| |
| /* |
| * look for |
| * unsafe.Sizeof |
| * unsafe.Offsetof |
| * unsafe.Alignof |
| * rewrite with a constant |
| */ |
| func unsafenmagic(nn *Node) *Node { |
| fn := nn.Left |
| args := nn.List |
| |
| if safemode != 0 || fn == nil || fn.Op != ONAME { |
| return nil |
| } |
| s := fn.Sym |
| if s == nil { |
| return nil |
| } |
| if s.Pkg != unsafepkg { |
| return nil |
| } |
| |
| if args == nil { |
| Yyerror("missing argument for %v", s) |
| return nil |
| } |
| |
| r := args.N |
| |
| var v int64 |
| if s.Name == "Sizeof" { |
| typecheck(&r, Erv) |
| defaultlit(&r, nil) |
| tr := r.Type |
| if tr == nil { |
| goto bad |
| } |
| dowidth(tr) |
| v = tr.Width |
| goto yes |
| } |
| |
| if s.Name == "Offsetof" { |
| // must be a selector. |
| if r.Op != OXDOT { |
| goto bad |
| } |
| |
| // Remember base of selector to find it back after dot insertion. |
| // Since r->left may be mutated by typechecking, check it explicitly |
| // first to track it correctly. |
| typecheck(&r.Left, Erv) |
| |
| base := r.Left |
| typecheck(&r, Erv) |
| switch r.Op { |
| case ODOT, ODOTPTR: |
| break |
| |
| case OCALLPART: |
| Yyerror("invalid expression %v: argument is a method value", nn) |
| v = 0 |
| goto ret |
| |
| default: |
| goto bad |
| } |
| |
| v = 0 |
| |
| // add offsets for inserted dots. |
| var r1 *Node |
| for r1 = r; r1.Left != base; r1 = r1.Left { |
| switch r1.Op { |
| case ODOT: |
| v += r1.Xoffset |
| |
| case ODOTPTR: |
| Yyerror("invalid expression %v: selector implies indirection of embedded %v", nn, r1.Left) |
| goto ret |
| |
| default: |
| Dump("unsafenmagic", r) |
| Fatal("impossible %v node after dot insertion", Oconv(int(r1.Op), obj.FmtSharp)) |
| goto bad |
| } |
| } |
| |
| v += r1.Xoffset |
| goto yes |
| } |
| |
| if s.Name == "Alignof" { |
| typecheck(&r, Erv) |
| defaultlit(&r, nil) |
| tr := r.Type |
| if tr == nil { |
| goto bad |
| } |
| |
| // make struct { byte; T; } |
| t := typ(TSTRUCT) |
| |
| t.Type = typ(TFIELD) |
| t.Type.Type = Types[TUINT8] |
| t.Type.Down = typ(TFIELD) |
| t.Type.Down.Type = tr |
| |
| // compute struct widths |
| dowidth(t) |
| |
| // the offset of T is its required alignment |
| v = t.Type.Down.Width |
| |
| goto yes |
| } |
| |
| return nil |
| |
| bad: |
| Yyerror("invalid expression %v", nn) |
| v = 0 |
| goto ret |
| |
| yes: |
| if args.Next != nil { |
| Yyerror("extra arguments for %v", s) |
| } |
| |
| // any side effects disappear; ignore init |
| ret: |
| var val Val |
| val.Ctype = CTINT |
| |
| val.U.Xval = new(Mpint) |
| Mpmovecfix(val.U.Xval, v) |
| n := Nod(OLITERAL, nil, nil) |
| n.Orig = nn |
| n.Val = 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 |
| } |