]> Cypherpunks repositories - gostls13.git/commitdiff
exp/ssa: add dedicated Panic instruction.
authorAlan Donovan <adonovan@google.com>
Thu, 21 Feb 2013 17:14:33 +0000 (12:14 -0500)
committerAlan Donovan <adonovan@google.com>
Thu, 21 Feb 2013 17:14:33 +0000 (12:14 -0500)
By avoiding the need for self-loops following calls to panic,
we reduce the number of basic blocks considerably.

R=gri
CC=golang-dev, iant
https://golang.org/cl/7403043

src/pkg/exp/ssa/builder.go
src/pkg/exp/ssa/emit.go
src/pkg/exp/ssa/interp/interp.go
src/pkg/exp/ssa/interp/ops.go
src/pkg/exp/ssa/print.go
src/pkg/exp/ssa/sanity.go
src/pkg/exp/ssa/ssa.go

index c8d56486e8ee82f3f38d73a77b1a68714126b5db..d8c17be50019ce8c7deb33e0e179c67915d2e676 100644 (file)
@@ -62,6 +62,7 @@ var (
        tInvalid    = types.Typ[types.Invalid]
        tUntypedNil = types.Typ[types.UntypedNil]
        tRangeIter  = &types.Basic{Name: "iter"} // the type of all "range" iterators
+       tEface      = new(types.Interface)
 
        // The result type of a "select".
        tSelect = &types.Result{Values: []*types.Var{
@@ -512,6 +513,11 @@ func (b *Builder) builtin(fn *Function, name string, args []ast.Expr, typ types.
                        return intLiteral(at.Len)
                }
                // Otherwise treat as normal.
+
+       case "panic":
+               fn.emit(&Panic{X: emitConv(fn, b.expr(fn, args[0]), tEface)})
+               fn.currentBlock = fn.newBasicBlock("unreachable")
+               return vFalse // any non-nil Value will do
        }
        return nil // treat all others as a regular function call
 }
@@ -774,32 +780,20 @@ func (b *Builder) expr(fn *Function, e ast.Expr) Value {
                        // Type conversion, e.g. string(x) or big.Int(x)
                        return emitConv(fn, b.expr(fn, e.Args[0]), typ)
                }
-               // Call to "intrinsic" built-ins, e.g. new, make.
-               wasPanic := false
+               // Call to "intrinsic" built-ins, e.g. new, make, panic.
                if id, ok := e.Fun.(*ast.Ident); ok {
                        obj := b.obj(id)
                        if _, ok := fn.Prog.Builtins[obj]; ok {
                                if v := b.builtin(fn, id.Name, e.Args, typ); v != nil {
                                        return v
                                }
-                               wasPanic = id.Name == "panic"
                        }
                }
                // Regular function call.
                var v Call
                b.setCall(fn, e, &v.CallCommon)
                v.setType(typ)
-               fn.emit(&v)
-
-               // Compile panic as if followed by for{} so that its
-               // successor is unreachable.
-               // TODO(adonovan): consider a dedicated Panic instruction
-               // (in which case, don't forget Go and Defer).
-               if wasPanic {
-                       emitSelfLoop(fn)
-                       fn.currentBlock = fn.newBasicBlock("unreachable")
-               }
-               return &v
+               return fn.emit(&v)
 
        case *ast.UnaryExpr:
                switch e.Op {
@@ -1161,7 +1155,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
                        bptypes = append(bptypes, nil) // map
                        bptypes = append(bptypes, nil) // key
                case "print", "println": // print{,ln}(any, ...any)
-                       vt = new(types.Interface) // variadic
+                       vt = tEface // variadic
                        if !c.HasEllipsis {
                                args, varargs = args[:1], args[1:]
                        }
@@ -1188,7 +1182,7 @@ func (b *Builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
                        }
                        bptypes = append(bptypes, argType, argType)
                case "panic":
-                       bptypes = append(bptypes, new(types.Interface))
+                       bptypes = append(bptypes, tEface)
                case "recover":
                        // no-op
                default:
@@ -2257,14 +2251,14 @@ start:
 
        case *ast.GoStmt:
                // The "intrinsics" new/make/len/cap are forbidden here.
-               // panic() is not forbidden, but is not (yet) an intrinsic.
+               // panic is treated like an ordinary function call.
                var v Go
                b.setCall(fn, s.Call, &v.CallCommon)
                fn.emit(&v)
 
        case *ast.DeferStmt:
                // The "intrinsics" new/make/len/cap are forbidden here.
-               // panic() is not forbidden, but is not (yet) an intrinsic.
+               // panic is treated like an ordinary function call.
                var v Defer
                b.setCall(fn, s.Call, &v.CallCommon)
                fn.emit(&v)
index 9a176b4f5086bdfe7ca9e078a7ed1df93803513c..d361463d9809d83ae7e806e81b7726bed5d6a4d5 100644 (file)
@@ -247,15 +247,3 @@ func emitTailCall(f *Function, call *Call) {
        f.emit(&ret)
        f.currentBlock = nil
 }
-
-// emitSelfLoop emits to f a self-loop.
-// This is a defensive measure to ensure control-flow integrity.
-// It should never be reachable.
-// Postcondition: f.currentBlock is nil.
-//
-func emitSelfLoop(f *Function) {
-       loop := f.newBasicBlock("selfloop")
-       emitJump(f, loop)
-       f.currentBlock = loop
-       emitJump(f, loop)
-}
index d2c54d91d67a86f06c1b1474db018aebcbd18393..a022996def92b5f9c049509a9ae86573372e17bd 100644 (file)
@@ -171,6 +171,9 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
                }
                return kReturn
 
+       case *ssa.Panic:
+               panic(targetPanic{fr.get(instr.X)})
+
        case *ssa.Send:
                fr.get(instr.Chan).(chan value) <- copyVal(fr.get(instr.X))
 
@@ -475,7 +478,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
 
        for {
                if i.mode&EnableTracing != 0 {
-                       fmt.Fprintf(os.Stderr, ".%s:\n", fr.block.Name)
+                       fmt.Fprintf(os.Stderr, ".%s:\n", fr.block)
                }
        block:
                for _, instr = range fr.block.Instrs {
index 783476c9b08e11d0fb29dc8faec6f9c2a90147e9..55cbf357325cb37b38a82fce60b9cfb0f56d6a03 100644 (file)
@@ -16,6 +16,9 @@ type targetPanic struct {
        v value
 }
 
+// If the target program calls exit, the interpreter panics with this type.
+type exitPanic int
+
 // literalValue returns the value of the literal with the
 // dynamic type tag appropriate for l.Type().
 func literalValue(l *ssa.Literal) value {
@@ -974,6 +977,8 @@ func callBuiltin(caller *frame, callpos token.Pos, fn *ssa.Builtin, args []value
                }
 
        case "panic":
+               // ssa.Panic handles most cases; this is only for "go
+               // panic" or "defer panic".
                panic(targetPanic{args[0]})
 
        case "recover":
index 69292ec22e05a3a2019d47bce82b3eb08e01fa9a..21303c168e6074f7fec4a0172b8924506cf13ab0 100644 (file)
@@ -282,6 +282,10 @@ func (s *Go) String() string {
        return printCall(&s.CallCommon, "go ", s)
 }
 
+func (s *Panic) String() string {
+       return "panic " + relName(s.X, s)
+}
+
 func (s *Ret) String() string {
        var b bytes.Buffer
        b.WriteString("ret")
index 8994f8e6bb0c73a78815dddc0c874d082e789057..9f8ba9f7a7dd64dc4607ff6a95bad1f74b011836 100644 (file)
@@ -96,7 +96,7 @@ func findDuplicate(blocks []*BasicBlock) *BasicBlock {
 
 func (s *sanity) checkInstr(idx int, instr Instruction) {
        switch instr := instr.(type) {
-       case *If, *Jump, *Ret:
+       case *If, *Jump, *Ret, *Panic:
                s.errorf("control flow instruction not at end of block")
        case *Phi:
                if idx == 0 {
@@ -192,6 +192,12 @@ func (s *sanity) checkFinalInstr(idx int, instr Instruction) {
                }
                // TODO(adonovan): check number and types of results
 
+       case *Panic:
+               if nsuccs := len(s.block.Succs); nsuccs != 0 {
+                       s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
+                       return
+               }
+
        default:
                s.errorf("non-control flow instruction at end of block")
        }
index 401c956b271b1f5e17d2be9fac897516f88e2f01..3bf047eee8a1cdede21aae66542d103cd35203ff 100644 (file)
@@ -248,7 +248,7 @@ type Function struct {
 // An SSA basic block.
 //
 // The final element of Instrs is always an explicit transfer of
-// control (If, Jump or Ret).
+// control (If, Jump, Ret or Panic).
 //
 // A block may contain no Instructions only if it is unreachable,
 // i.e. Preds is nil.  Empty blocks are typically pruned.
@@ -842,6 +842,22 @@ type Ret struct {
        Results []Value
 }
 
+// Panic initiates a panic with value X.
+//
+// A Panic instruction must be the last instruction of its containing
+// BasicBlock, which must have no successors.
+//
+// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;
+// they are treated as calls to a built-in function.
+//
+// Example printed form:
+//     panic t0
+//
+type Panic struct {
+       anInstruction
+       X Value // an interface{}
+}
+
 // Go creates a new goroutine and calls the specified function
 // within it.
 //
@@ -1125,6 +1141,7 @@ func (*MakeMap) ImplementsInstruction()         {}
 func (*MakeSlice) ImplementsInstruction()       {}
 func (*MapUpdate) ImplementsInstruction()       {}
 func (*Next) ImplementsInstruction()            {}
+func (*Panic) ImplementsInstruction()           {}
 func (*Phi) ImplementsInstruction()             {}
 func (*Range) ImplementsInstruction()           {}
 func (*Ret) ImplementsInstruction()             {}
@@ -1227,6 +1244,10 @@ func (v *Next) Operands(rands []*Value) []*Value {
        return append(rands, &v.Iter)
 }
 
+func (s *Panic) Operands(rands []*Value) []*Value {
+       return append(rands, &s.X)
+}
+
 func (v *Phi) Operands(rands []*Value) []*Value {
        for i := range v.Edges {
                rands = append(rands, &v.Edges[i])