1
0
mirror of https://github.com/golang/go synced 2024-10-05 16:41:21 -06:00

[dev.ssa] cmd/compile: implement ODOTTYPE and OAS2DOTTYPE

Taken over and completed from Josh's change
https://go-review.googlesource.com/#/c/14524/

Change-Id: If5d4f732843cc3e99bd5edda54458f0a8be73e91
Reviewed-on: https://go-review.googlesource.com/14690
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
Keith Randall 2015-09-17 10:31:16 -07:00
parent 5f10573e60
commit 269baa981e
3 changed files with 286 additions and 5 deletions

View File

@ -259,12 +259,17 @@ func (s *state) Logf(msg string, args ...interface{}) { s.config.Logf(
func (s *state) Fatalf(msg string, args ...interface{}) { s.config.Fatalf(msg, args...) }
func (s *state) Unimplementedf(msg string, args ...interface{}) { s.config.Unimplementedf(msg, args...) }
// dummy node for the memory variable
var memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}}
var (
// dummy node for the memory variable
memvar = Node{Op: ONAME, Sym: &Sym{Name: "mem"}}
// dummy nodes for temporary variables
var ptrvar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}}
var capvar = Node{Op: ONAME, Sym: &Sym{Name: "cap"}}
// dummy nodes for temporary variables
ptrvar = Node{Op: ONAME, Sym: &Sym{Name: "ptr"}}
capvar = Node{Op: ONAME, Sym: &Sym{Name: "cap"}}
typVar = Node{Op: ONAME, Sym: &Sym{Name: "typ"}}
idataVar = Node{Op: ONAME, Sym: &Sym{Name: "idata"}}
okVar = Node{Op: ONAME, Sym: &Sym{Name: "ok"}}
)
// startBlock sets the current block we're generating code in to b.
func (s *state) startBlock(b *ssa.Block) {
@ -474,6 +479,12 @@ func (s *state) stmt(n *Node) {
case OPROC:
s.call(n.Left, callGo)
case OAS2DOTTYPE:
res, resok := s.dottype(n.Rlist.N, true)
s.assign(n.List.N, res, false)
s.assign(n.List.Next.N, resok, false)
return
case ODCL:
if n.Left.Class&PHEAP == 0 {
return
@ -1471,6 +1482,10 @@ func (s *state) expr(n *Node) *ssa.Value {
s.Unimplementedf("unhandled OCONV %s -> %s", Econv(int(n.Left.Type.Etype), 0), Econv(int(n.Type.Etype), 0))
return nil
case ODOTTYPE:
res, _ := s.dottype(n, false)
return res
// binary ops
case OLT, OEQ, ONE, OLE, OGE, OGT:
a := s.expr(n.Left)
@ -2723,6 +2738,122 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *Ty
return s.variable(n, n.Type)
}
// ifaceType returns the value for the word containing the type.
// n is the node for the interface expression.
// v is the corresponding value.
func (s *state) ifaceType(n *Node, v *ssa.Value) *ssa.Value {
byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
if isnilinter(n.Type) {
// Have *eface. The type is the first word in the struct.
return s.newValue1(ssa.OpITab, byteptr, v)
}
// Have *iface.
// The first word in the struct is the *itab.
// If the *itab is nil, return 0.
// Otherwise, the second word in the *itab is the type.
tab := s.newValue1(ssa.OpITab, byteptr, v)
s.vars[&typVar] = tab
isnonnil := s.newValue2(ssa.OpNeqPtr, Types[TBOOL], tab, s.entryNewValue0(ssa.OpConstNil, byteptr))
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Control = isnonnil
b.Likely = ssa.BranchLikely
bLoad := s.f.NewBlock(ssa.BlockPlain)
bEnd := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bLoad)
b.AddEdgeTo(bEnd)
bLoad.AddEdgeTo(bEnd)
s.startBlock(bLoad)
off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), tab)
s.vars[&typVar] = s.newValue2(ssa.OpLoad, byteptr, off, s.mem())
s.endBlock()
s.startBlock(bEnd)
typ := s.variable(&typVar, byteptr)
delete(s.vars, &typVar)
return typ
}
// dottype generates SSA for a type assertion node.
// commaok indicates whether to panic or return a bool.
// If commaok is false, resok will be nil.
func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
iface := s.expr(n.Left)
typ := s.ifaceType(n.Left, iface) // actual concrete type
target := s.expr(typename(n.Type)) // target type
if !isdirectiface(n.Type) {
// walk rewrites ODOTTYPE/OAS2DOTTYPE into runtime calls except for this case.
Fatalf("dottype needs a direct iface type %s", n.Type)
}
// TODO: If we have a nonempty interface and its itab field is nil,
// then this test is redundant and ifaceType should just branch directly to bFail.
cond := s.newValue2(ssa.OpEqPtr, Types[TBOOL], typ, target)
b := s.endBlock()
b.Kind = ssa.BlockIf
b.Control = cond
b.Likely = ssa.BranchLikely
byteptr := Ptrto(Types[TUINT8])
bOk := s.f.NewBlock(ssa.BlockPlain)
bFail := s.f.NewBlock(ssa.BlockPlain)
b.AddEdgeTo(bOk)
b.AddEdgeTo(bFail)
if !commaok {
// on failure, panic by calling panicdottype
s.startBlock(bFail)
spplus1 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(Widthptr), s.sp)
spplus2 := s.newValue1I(ssa.OpOffPtr, Types[TUINTPTR], int64(2*Widthptr), s.sp)
taddr := s.newValue1A(ssa.OpAddr, byteptr, &ssa.ExternSymbol{byteptr, typenamesym(n.Left.Type)}, s.sb)
s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), s.sp, typ, s.mem()) // actual dynamic type
s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus1, target, s.mem()) // type we're casting to
s.vars[&memvar] = s.newValue3I(ssa.OpStore, ssa.TypeMem, int64(Widthptr), spplus2, taddr, s.mem()) // static source type
call := s.newValue1A(ssa.OpStaticCall, ssa.TypeMem, syslook("panicdottype", 0).Sym, s.mem())
s.endBlock()
bFail.Kind = ssa.BlockExit
bFail.Control = call
// on success, return idata field
s.startBlock(bOk)
return s.newValue1(ssa.OpIData, n.Type, iface), nil
}
// commaok is the more complicated case because we have
// a control flow merge point.
bEnd := s.f.NewBlock(ssa.BlockPlain)
// type assertion succeeded
s.startBlock(bOk)
s.vars[&idataVar] = s.newValue1(ssa.OpIData, n.Type, iface)
s.vars[&okVar] = s.constBool(true)
s.endBlock()
bOk.AddEdgeTo(bEnd)
// type assertion failed
s.startBlock(bFail)
s.vars[&idataVar] = s.entryNewValue0(ssa.OpConstNil, byteptr)
s.vars[&okVar] = s.constBool(false)
s.endBlock()
bFail.AddEdgeTo(bEnd)
// merge point
s.startBlock(bEnd)
res = s.variable(&idataVar, byteptr)
resok = s.variable(&okVar, Types[TBOOL])
delete(s.vars, &idataVar)
delete(s.vars, &okVar)
return res, resok
}
// checkgoto checks that a goto from from to to does not
// jump into a block or jump over variable declarations.
// It is a copy of checkgoto in the pre-SSA backend,

View File

@ -48,6 +48,9 @@ func TestShortCircuit(t *testing.T) { runTest(t, "short_ssa.go") }
// TestBreakContinue tests that continue and break statements do what they say.
func TestBreakContinue(t *testing.T) { runTest(t, "break_ssa.go") }
// TestTypeAssertion tests type assertions.
func TestTypeAssertion(t *testing.T) { runTest(t, "assert_ssa.go") }
// TestArithmetic tests that both backends have the same result for arithmetic expressions.
func TestArithmetic(t *testing.T) { runTest(t, "arith_ssa.go") }

View File

@ -0,0 +1,147 @@
// run
// Copyright 2015 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.
// Tests type assertion expressions and statements
package main
import (
"fmt"
"runtime"
)
type (
S struct{}
T struct{}
I interface {
F()
}
)
var (
s *S
t *T
)
func (s *S) F() {}
func (t *T) F() {}
func e2t_ssa(e interface{}) *T {
return e.(*T)
}
func i2t_ssa(i I) *T {
return i.(*T)
}
func testAssertE2TOk() {
if got := e2t_ssa(t); got != t {
fmt.Printf("e2t_ssa(t)=%v want %v", got, t)
failed = true
}
}
func testAssertE2TPanic() {
var got *T
defer func() {
if got != nil {
fmt.Printf("e2t_ssa(s)=%v want nil", got)
failed = true
}
e := recover()
err, ok := e.(*runtime.TypeAssertionError)
if !ok {
fmt.Printf("e2t_ssa(s) panic type %T", e)
failed = true
}
want := "interface conversion: interface {} is *main.S, not *main.T"
if err.Error() != want {
fmt.Printf("e2t_ssa(s) wrong error, want '%s', got '%s'\n", want, err.Error())
failed = true
}
}()
got = e2t_ssa(s)
fmt.Printf("e2t_ssa(s) should panic")
failed = true
}
func testAssertI2TOk() {
if got := i2t_ssa(t); got != t {
fmt.Printf("i2t_ssa(t)=%v want %v", got, t)
failed = true
}
}
func testAssertI2TPanic() {
var got *T
defer func() {
if got != nil {
fmt.Printf("i2t_ssa(s)=%v want nil", got)
failed = true
}
e := recover()
err, ok := e.(*runtime.TypeAssertionError)
if !ok {
fmt.Printf("i2t_ssa(s) panic type %T", e)
failed = true
}
want := "interface conversion: main.I is *main.S, not *main.T"
if err.Error() != want {
fmt.Printf("i2t_ssa(s) wrong error, want '%s', got '%s'\n", want, err.Error())
failed = true
}
}()
got = i2t_ssa(s)
fmt.Printf("i2t_ssa(s) should panic")
failed = true
}
func e2t2_ssa(e interface{}) (*T, bool) {
t, ok := e.(*T)
return t, ok
}
func i2t2_ssa(i I) (*T, bool) {
t, ok := i.(*T)
return t, ok
}
func testAssertE2T2() {
if got, ok := e2t2_ssa(t); !ok || got != t {
fmt.Printf("e2t2_ssa(t)=(%v, %v) want (%v, %v)", got, ok, t, true)
failed = true
}
if got, ok := e2t2_ssa(s); ok || got != nil {
fmt.Printf("e2t2_ssa(s)=(%v, %v) want (%v, %v)", got, ok, nil, false)
failed = true
}
}
func testAssertI2T2() {
if got, ok := i2t2_ssa(t); !ok || got != t {
fmt.Printf("i2t2_ssa(t)=(%v, %v) want (%v, %v)", got, ok, t, true)
failed = true
}
if got, ok := i2t2_ssa(s); ok || got != nil {
fmt.Printf("i2t2_ssa(s)=(%v, %v) want (%v, %v)", got, ok, nil, false)
failed = true
}
}
var failed = false
func main() {
testAssertE2TOk()
testAssertE2TPanic()
testAssertI2TOk()
testAssertI2TPanic()
testAssertE2T2()
testAssertI2T2()
if failed {
panic("failed")
}
}