// 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.U = new(Mpint)
	Mpmovecfix(val.U.(*Mpint), 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
}
