s.pushLine(fn.Lineno)
defer s.popLine()
+ if fn.Func.Pragma&CgoUnsafeArgs != 0 {
+ s.cgoUnsafeArgs = true
+ }
// TODO(khr): build config just once at the start of the compiler binary
ssaExp.log = printssa
s.decladdrs = map[*Node]*ssa.Value{}
for _, n := range fn.Func.Dcl {
switch n.Class {
- case PPARAM:
+ case PPARAM, PPARAMOUT:
aux := s.lookupSymbol(n, &ssa.ArgSymbol{Typ: n.Type, Node: n})
s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
+ if n.Class == PPARAMOUT && s.canSSA(n) {
+ // Save ssa-able PPARAMOUT variables so we can
+ // store them back to the stack at the end of
+ // the function.
+ s.returns = append(s.returns, n)
+ }
case PAUTO | PHEAP:
// TODO this looks wrong for PAUTO|PHEAP, no vardef, but also no definition
aux := s.lookupSymbol(n, &ssa.AutoSymbol{Typ: n.Type, Node: n})
s.decladdrs[n] = s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
case PPARAM | PHEAP, PPARAMOUT | PHEAP:
// This ends up wrong, have to do it at the PARAM node instead.
- case PAUTO, PPARAMOUT:
+ case PAUTO:
// processed at each use, to prevent Addr coming
// before the decl.
case PFUNC:
// list of FwdRef values.
fwdRefs []*ssa.Value
+
+ // list of PPARAMOUT (return) variables. Does not include PPARAM|PHEAP vars.
+ returns []*Node
+
+ cgoUnsafeArgs bool
}
type funcLine struct {
s.call(n, callNormal)
if n.Op == OCALLFUNC && n.Left.Op == ONAME && n.Left.Class == PFUNC &&
(compiling_runtime != 0 && n.Left.Sym.Name == "throw" ||
- n.Left.Sym.Pkg == Runtimepkg && (n.Left.Sym.Name == "gopanic" || n.Left.Sym.Name == "selectgo")) {
+ n.Left.Sym.Pkg == Runtimepkg && (n.Left.Sym.Name == "gopanic" || n.Left.Sym.Name == "selectgo" || n.Left.Sym.Name == "block")) {
m := s.mem()
b := s.endBlock()
b.Kind = ssa.BlockExit
case ORETURN:
s.stmtList(n.List)
- s.stmts(s.exitCode)
- m := s.mem()
- b := s.endBlock()
- b.Kind = ssa.BlockRet
- b.Control = m
+ s.exit()
case ORETJMP:
s.stmtList(n.List)
- s.stmts(s.exitCode)
- m := s.mem()
- b := s.endBlock()
- b.Kind = ssa.BlockRetJmp
+ b := s.exit()
+ b.Kind = ssa.BlockRetJmp // override BlockRet
b.Aux = n.Left.Sym
- b.Control = m
case OCONTINUE, OBREAK:
var op string
// We only care about liveness info at call sites, so putting the
// varkill in the store chain is enough to keep it correctly ordered
// with respect to call ops.
- if !canSSA(n.Left) {
+ if !s.canSSA(n.Left) {
s.vars[&memVar] = s.newValue1A(ssa.OpVarKill, ssa.TypeMem, n.Left, s.mem())
}
}
}
+// exit processes any code that needs to be generated just before returning.
+// It returns a BlockRet block that ends the control flow. Its control value
+// will be set to the final memory state.
+func (s *state) exit() *ssa.Block {
+ // Run exit code. Typically, this code copies heap-allocated PPARAMOUT
+ // variables back to the stack.
+ s.stmts(s.exitCode)
+
+ // Store SSAable PPARAMOUT variables back to stack locations.
+ for _, n := range s.returns {
+ aux := &ssa.ArgSymbol{Typ: n.Type, Node: n}
+ addr := s.newValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sp)
+ val := s.variable(n, n.Type)
+ s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, n, s.mem())
+ s.vars[&memVar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, n.Type.Size(), addr, val, s.mem())
+ // TODO: if val is ever spilled, we'd like to use the
+ // PPARAMOUT slot for spilling it. That won't happen
+ // currently.
+ }
+
+ // Do actual return.
+ m := s.mem()
+ b := s.endBlock()
+ b.Kind = ssa.BlockRet
+ b.Control = m
+ return b
+}
+
type opAndType struct {
op Op
etype EType
aux := &ssa.ExternSymbol{n.Type, sym}
return s.entryNewValue1A(ssa.OpAddr, Ptrto(n.Type), aux, s.sb)
}
- if canSSA(n) {
+ if s.canSSA(n) {
return s.variable(n, n.Type)
}
addr := s.addr(n, false)
}
t := left.Type
dowidth(t)
- if canSSA(left) {
+ if s.canSSA(left) {
if deref {
s.Fatalf("can SSA LHS %s but not RHS %s", left, right)
}
// canSSA reports whether n is SSA-able.
// n must be an ONAME (or an ODOT sequence with an ONAME base).
-func canSSA(n *Node) bool {
+func (s *state) canSSA(n *Node) bool {
for n.Op == ODOT {
n = n.Left
}
return false
}
switch n.Class {
- case PEXTERN, PPARAMOUT, PPARAMREF:
+ case PEXTERN, PPARAMREF:
+ // TODO: maybe treat PPARAMREF with an Arg-like op to read from closure?
return false
+ case PPARAMOUT:
+ if hasdefer {
+ // TODO: handle this case? Named return values must be
+ // in memory so that the deferred function can see them.
+ // Maybe do: if !strings.HasPrefix(n.String(), "~") { return false }
+ return false
+ }
+ if s.cgoUnsafeArgs {
+ // Cgo effectively takes the address of all result args,
+ // but the compiler can't see that.
+ return false
+ }
}
if n.Class == PPARAM && n.String() == ".this" {
// wrappers generated by genwrapper need to update
// the .this pointer in place.
+ // TODO: treat as a PPARMOUT?
return false
}
return canSSAType(n.Type)
v.Aux = nil
if b == s.f.Entry {
// Live variable at start of function.
- if canSSA(name) {
+ if s.canSSA(name) {
v.Op = ssa.OpArg
v.Aux = name
return
p.From.Node = n
p.From.Sym = Linksym(n.Sym)
p.From.Offset = off
- if n.Class == PPARAM {
+ if n.Class == PPARAM || n.Class == PPARAMOUT {
p.From.Name = obj.NAME_PARAM
p.From.Offset += n.Xoffset
} else {
p.To.Node = n
p.To.Sym = Linksym(n.Sym)
p.To.Offset = off
- if n.Class == PPARAM {
+ if n.Class == PPARAM || n.Class == PPARAMOUT {
p.To.Name = obj.NAME_PARAM
p.To.Offset += n.Xoffset
} else {