Commit 483cb619 authored by Keith Randall's avatar Keith Randall

runtime: convert interface routines from C to Go.

LGTM=dvyukov
R=golang-codereviews, dave, bradfitz, dvyukov, khr
CC=golang-codereviews
https://golang.org/cl/98510044
parent 5ecbdb04
......@@ -378,7 +378,7 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
}
if w.context != nil && file == fmt.Sprintf("zruntime_defs_%s_%s.go", w.context.GOOS, w.context.GOARCH) {
// Just enough to keep the api checker happy.
src := "package runtime; type maptype struct{}; type _type struct{}; type alg struct{}; type mspan struct{}; type m struct{}; type lock struct{}; type slicetype struct{}; type iface struct{}; type eface struct{}"
src := "package runtime; type maptype struct{}; type _type struct{}; type alg struct{}; type mspan struct{}; type m struct{}; type lock struct{}; type slicetype struct{}; type iface struct{}; type eface struct{}; type interfacetype struct{}; type itab struct{}"
f, err = parser.ParseFile(fset, filename, src, 0)
if err != nil {
log.Fatalf("incorrect generated file: %s", err)
......
......@@ -46,3 +46,9 @@ TEXT ·maplen(SB),NOSPLIT,$0-0
JMP runtime·reflect_maplen(SB)
TEXT ·ismapkey(SB),NOSPLIT,$0-0
JMP runtime·reflect_ismapkey(SB)
TEXT ·ifaceE2I(SB),NOSPLIT,$0-0
JMP runtime·reflect_ifaceE2I(SB)
TEXT ·unsafe_New(SB),NOSPLIT,$0-0
JMP runtime·newobject(SB)
TEXT ·unsafe_NewArray(SB),NOSPLIT,$0-0
JMP runtime·newarray(SB)
......@@ -46,3 +46,9 @@ TEXT ·maplen(SB),NOSPLIT,$0-0
JMP runtime·reflect_maplen(SB)
TEXT ·ismapkey(SB),NOSPLIT,$0-0
JMP runtime·reflect_ismapkey(SB)
TEXT ·ifaceE2I(SB),NOSPLIT,$0-0
JMP runtime·reflect_ifaceE2I(SB)
TEXT ·unsafe_New(SB),NOSPLIT,$0-0
JMP runtime·newobject(SB)
TEXT ·unsafe_NewArray(SB),NOSPLIT,$0-0
JMP runtime·newarray(SB)
......@@ -46,3 +46,9 @@ TEXT ·maplen(SB),NOSPLIT,$0-0
JMP runtime·reflect_maplen(SB)
TEXT ·ismapkey(SB),NOSPLIT,$0-0
JMP runtime·reflect_ismapkey(SB)
TEXT ·ifaceE2I(SB),NOSPLIT,$0-0
JMP runtime·reflect_ifaceE2I(SB)
TEXT ·unsafe_New(SB),NOSPLIT,$0-0
JMP runtime·newobject(SB)
TEXT ·unsafe_NewArray(SB),NOSPLIT,$0-0
JMP runtime·newarray(SB)
......@@ -46,3 +46,9 @@ TEXT ·maplen(SB),NOSPLIT,$-4-0
B runtime·reflect_maplen(SB)
TEXT ·ismapkey(SB),NOSPLIT,$-4-0
B runtime·reflect_ismapkey(SB)
TEXT ·ifaceE2I(SB),NOSPLIT,$0-0
B runtime·reflect_ifaceE2I(SB)
TEXT ·unsafe_New(SB),NOSPLIT,$0-0
B runtime·newobject(SB)
TEXT ·unsafe_NewArray(SB),NOSPLIT,$0-0
B runtime·newarray(SB)
......@@ -308,6 +308,7 @@ runtime·nilintercopy(uintptr s, void *a, void *b)
}
extern uintptr runtime·nohashcode;
extern uintptr runtime·noequalcode;
void
runtime·noequal(bool *eq, uintptr s, void *a, void *b)
......@@ -371,6 +372,8 @@ void
runtime·hashinit(void)
{
runtime·nohashcode = (uintptr)runtime·nohash;
runtime·noequalcode = (uintptr)runtime·noequal;
if(NaCl)
return;
......
This diff is collapsed.
......@@ -10,9 +10,10 @@ package runtime
#include "malloc.h"
#include "../../cmd/ld/textflag.h"
static Itab* hash[1009];
static Lock ifacelock;
extern Itab* runtime·hash[1009];
extern Lock runtime·ifaceLock;
// TODO: delete this when no longer used (ifaceE2I2 is all that's left)
static Itab*
itab(InterfaceType *inter, Type *type, int32 canfail)
{
......@@ -45,14 +46,14 @@ itab(InterfaceType *inter, Type *type, int32 canfail)
h = inter->typ.hash;
h += 17 * type->hash;
// TODO(rsc): h += 23 * x->mhash ?
h %= nelem(hash);
h %= nelem(runtime·hash);
// look twice - once without lock, once with.
// common case will be no lock contention.
for(locked=0; locked<2; locked++) {
if(locked)
runtime·lock(&ifacelock);
for(m=runtime·atomicloadp(&hash[h]); m!=nil; m=m->link) {
runtime·lock(&runtime·ifaceLock);
for(m=runtime·atomicloadp(&runtime·hash[h]); m!=nil; m=m->link) {
if(m->inter == inter && m->type == type) {
if(m->bad) {
m = nil;
......@@ -68,7 +69,7 @@ itab(InterfaceType *inter, Type *type, int32 canfail)
}
}
if(locked)
runtime·unlock(&ifacelock);
runtime·unlock(&runtime·ifaceLock);
return m;
}
}
......@@ -101,7 +102,7 @@ search:
nil, type->string, inter->typ.string,
iname, &err);
if(locked)
runtime·unlock(&ifacelock);
runtime·unlock(&runtime·ifaceLock);
runtime·panic(err);
return nil; // not reached
}
......@@ -118,9 +119,9 @@ search:
out:
if(!locked)
runtime·panicstring("invalid itab locking");
m->link = hash[h];
runtime·atomicstorep(&hash[h], m);
runtime·unlock(&ifacelock);
m->link = runtime·hash[h];
runtime·atomicstorep(&runtime·hash[h], m);
runtime·unlock(&runtime·ifaceLock);
if(m->bad)
return nil;
return m;
......@@ -133,295 +134,16 @@ runtime·iterate_itabs(void (*callback)(Itab*))
int32 i;
Itab *tab;
for(i = 0; i < nelem(hash); i++) {
for(tab = hash[i]; tab != nil; tab = tab->link) {
for(i = 0; i < nelem(runtime·hash); i++) {
for(tab = runtime·hash[i]; tab != nil; tab = tab->link) {
callback(tab);
}
}
}
static void
copyin(Type *t, void *src, void **dst)
{
uintptr size;
void *p;
Alg *alg;
size = t->size;
alg = t->alg;
if(size <= sizeof(*dst))
alg->copy(size, dst, src);
else {
p = runtime·cnew(t);
alg->copy(size, p, src);
*dst = p;
}
}
static void
copyout(Type *t, void **src, void *dst)
{
uintptr size;
Alg *alg;
size = t->size;
alg = t->alg;
if(size <= sizeof(*src))
alg->copy(size, dst, src);
else
alg->copy(size, dst, *src);
}
#pragma textflag NOSPLIT
func typ2Itab(t *Type, inter *InterfaceType, cache **Itab) (tab *Itab) {
tab = itab(inter, t, 0);
runtime·atomicstorep(cache, tab);
}
#pragma textflag NOSPLIT
func convT2I(t *Type, inter *InterfaceType, cache **Itab, elem *byte) (ret Iface) {
Itab *tab;
tab = runtime·atomicloadp(cache);
if(!tab) {
tab = itab(inter, t, 0);
runtime·atomicstorep(cache, tab);
}
ret.tab = tab;
copyin(t, elem, &ret.data);
}
#pragma textflag NOSPLIT
func convT2E(t *Type, elem *byte) (ret Eface) {
ret.type = t;
copyin(t, elem, &ret.data);
}
static void assertI2Tret(Type *t, Iface i, byte *ret);
/*
* NOTE: Cannot use 'func' here, because we have to declare
* a return value, the only types we have are at least 1 byte large,
* goc2c will zero the return value, and the actual return value
* might have size 0 bytes, in which case the zeroing of the
* 1 or more bytes would be wrong.
* Using C lets us control (avoid) the initial zeroing.
*/
#pragma textflag NOSPLIT
void
runtime·assertI2T(Type *t, Iface i, GoOutput retbase)
{
assertI2Tret(t, i, (byte*)&retbase);
}
static void
assertI2Tret(Type *t, Iface i, byte *ret)
{
Itab *tab;
Eface err;
tab = i.tab;
if(tab == nil) {
runtime·newTypeAssertionError(
nil, nil, t->string,
nil, &err);
runtime·panic(err);
}
if(tab->type != t) {
runtime·newTypeAssertionError(
tab->inter->typ.string, tab->type->string, t->string,
nil, &err);
runtime·panic(err);
}
copyout(t, &i.data, ret);
}
#pragma textflag NOSPLIT
func assertI2T2(t *Type, i Iface) (ret byte, ...) {
bool *ok;
int32 wid;
wid = t->size;
ok = (bool*)(&ret + wid);
if(i.tab == nil || i.tab->type != t) {
*ok = false;
runtime·memclr(&ret, wid);
return;
}
*ok = true;
copyout(t, &i.data, &ret);
}
func assertI2TOK(t *Type, i Iface) (ok bool) {
ok = i.tab!=nil && i.tab->type==t;
}
static void assertE2Tret(Type *t, Eface e, byte *ret);
/*
* NOTE: Cannot use 'func' here. See assertI2T above.
*/
#pragma textflag NOSPLIT
void
runtime·assertE2T(Type *t, Eface e, GoOutput retbase)
{
assertE2Tret(t, e, (byte*)&retbase);
}
static void
assertE2Tret(Type *t, Eface e, byte *ret)
{
Eface err;
if(e.type == nil) {
runtime·newTypeAssertionError(
nil, nil, t->string,
nil, &err);
runtime·panic(err);
}
if(e.type != t) {
runtime·newTypeAssertionError(
nil, e.type->string, t->string,
nil, &err);
runtime·panic(err);
}
copyout(t, &e.data, ret);
}
#pragma textflag NOSPLIT
func assertE2T2(t *Type, e Eface) (ret byte, ...) {
bool *ok;
int32 wid;
wid = t->size;
ok = (bool*)(&ret + wid);
if(t != e.type) {
*ok = false;
runtime·memclr(&ret, wid);
return;
}
*ok = true;
copyout(t, &e.data, &ret);
}
func assertE2TOK(t *Type, e Eface) (ok bool) {
ok = t==e.type;
}
func convI2E(i Iface) (ret Eface) {
Itab *tab;
ret.data = i.data;
if((tab = i.tab) == nil)
ret.type = nil;
else
ret.type = tab->type;
}
func assertI2E(inter *InterfaceType, i Iface) (ret Eface) {
Itab *tab;
Eface err;
tab = i.tab;
if(tab == nil) {
// explicit conversions require non-nil interface value.
runtime·newTypeAssertionError(
nil, nil, inter->typ.string,
nil, &err);
runtime·panic(err);
}
ret.data = i.data;
ret.type = tab->type;
}
func assertI2E2(inter *InterfaceType, i Iface) (ret Eface, ok bool) {
Itab *tab;
USED(inter);
tab = i.tab;
if(tab == nil) {
ret.type = nil;
ok = 0;
} else {
ret.type = tab->type;
ok = 1;
}
ret.data = i.data;
}
func convI2I(inter *InterfaceType, i Iface) (ret Iface) {
Itab *tab;
ret.data = i.data;
if((tab = i.tab) == nil)
ret.tab = nil;
else if(tab->inter == inter)
ret.tab = tab;
else
ret.tab = itab(inter, tab->type, 0);
}
void
runtime·ifaceI2I(InterfaceType *inter, Iface i, Iface *ret)
{
Itab *tab;
Eface err;
tab = i.tab;
if(tab == nil) {
// explicit conversions require non-nil interface value.
runtime·newTypeAssertionError(
nil, nil, inter->typ.string,
nil, &err);
runtime·panic(err);
}
ret->data = i.data;
ret->tab = itab(inter, tab->type, 0);
}
func assertI2I(inter *InterfaceType, i Iface) (ret Iface) {
runtime·ifaceI2I(inter, i, &ret);
}
func assertI2I2(inter *InterfaceType, i Iface) (ret Iface, ok bool) {
Itab *tab;
tab = i.tab;
if(tab != nil && (tab->inter == inter || (tab = itab(inter, tab->type, 1)) != nil)) {
ret.data = i.data;
ret.tab = tab;
ok = 1;
} else {
ret.data = 0;
ret.tab = 0;
ok = 0;
}
}
void
runtime·ifaceE2I(InterfaceType *inter, Eface e, Iface *ret)
{
Type *t;
Eface err;
t = e.type;
if(t == nil) {
// explicit conversions require non-nil interface value.
runtime·newTypeAssertionError(
nil, nil, inter->typ.string,
nil, &err);
runtime·panic(err);
}
ret->data = e.data;
ret->tab = itab(inter, t, 0);
}
// Still in C because it is called from C for finalizers. This will
// get converted to Go in a separate CL. This is the last user of
// the C version of itab().
bool
runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
{
......@@ -432,49 +154,6 @@ runtime·ifaceE2I2(InterfaceType *inter, Eface e, Iface *ret)
return true;
}
func reflect·ifaceE2I(inter *InterfaceType, e Eface, dst *Iface) {
runtime·ifaceE2I(inter, e, dst);
}
func assertE2I(inter *InterfaceType, e Eface) (ret Iface) {
runtime·ifaceE2I(inter, e, &ret);
}
func assertE2I2(inter *InterfaceType, e Eface) (ret Iface, ok bool) {
if(e.type == nil) {
ok = 0;
ret.data = nil;
ret.tab = nil;
} else if((ret.tab = itab(inter, e.type, 1)) == nil) {
ok = 0;
ret.data = nil;
} else {
ok = 1;
ret.data = e.data;
}
}
func assertE2E(inter *InterfaceType, e Eface) (ret Eface) {
Type *t;
Eface err;
t = e.type;
if(t == nil) {
// explicit conversions require non-nil interface value.
runtime·newTypeAssertionError(
nil, nil, inter->typ.string,
nil, &err);
runtime·panic(err);
}
ret = e;
}
func assertE2E2(inter *InterfaceType, e Eface) (ret Eface, ok bool) {
USED(inter);
ret = e;
ok = e.type != nil;
}
static bool
ifaceeq1(void *data1, void *data2, Type *t)
{
......@@ -520,54 +199,3 @@ runtime·efaceeq_c(Eface e1, Eface e2)
return true;
return ifaceeq1(e1.data, e2.data, e1.type);
}
func ifaceeq(i1 Iface, i2 Iface) (ret bool) {
ret = runtime·ifaceeq_c(i1, i2);
}
func efaceeq(e1 Eface, e2 Eface) (ret bool) {
ret = runtime·efaceeq_c(e1, e2);
}
func ifacethash(i1 Iface) (ret uint32) {
Itab *tab;
ret = 0;
tab = i1.tab;
if(tab != nil)
ret = tab->type->hash;
}
func efacethash(e1 Eface) (ret uint32) {
Type *t;
ret = 0;
t = e1.type;
if(t != nil)
ret = t->hash;
}
func reflect·unsafe_Typeof(e Eface) (ret Eface) {
if(e.type == nil) {
ret.type = nil;
ret.data = nil;
} else {
ret = *(Eface*)(e.type);
}
}
func reflect·unsafe_New(t *Type) (ret *byte) {
ret = runtime·cnew(t);
}
func reflect·unsafe_NewArray(t *Type, n int) (ret *byte) {
ret = runtime·cnewarray(t, n);
}
func reflect·typelinks() (ret Slice) {
extern Type *typelink[], *etypelink[];
static int32 first = 1;
ret.array = (byte*)typelink;
ret.len = etypelink - typelink;
ret.cap = ret.len;
}
......@@ -72,6 +72,7 @@ var (
// memclr clears n bytes starting at ptr.
// in memclr_*.s
//go:noescape
func memclr(ptr unsafe.Pointer, n uintptr)
func racemalloc(p unsafe.Pointer, size uintptr)
......@@ -79,6 +80,7 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type)
// memmove copies n bytes from "from" to "to".
// in memmove_*.s
//go:noescape
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
// in asm_*.s
......@@ -124,8 +126,9 @@ var hashLoad = loadFactor
//go:noescape
func gomemeq(a, b unsafe.Pointer, size uintptr) bool
// Code pointer for the nohash algorithm. Used for producing better error messages.
// Code pointers for the nohash/noequal algorithms. Used for producing better error messages.
var nohashcode uintptr
var noequalcode uintptr
// Go version of runtime.throw.
// in panic.c
......@@ -159,3 +162,7 @@ func noescape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
return unsafe.Pointer(x ^ 0)
}
// gopersistentalloc allocates a permanent (not garbage collected)
// memory region of size n. Use wisely!
func gopersistentalloc(n uintptr) unsafe.Pointer
......@@ -89,3 +89,17 @@ func GCMask(x Eface) (mask Slice) {
runtime·getgcmask(x.data, x.type, &mask.array, &mask.len);
mask.cap = mask.len;
}
#pragma textflag NOSPLIT
func gopersistentalloc(size uintptr) (x *void) {
// TODO: used only for itabs for now. Need to make &mstats.other_sys arg parameterized.
x = runtime·persistentalloc(size, 0, &mstats.other_sys);
}
#pragma textflag NOSPLIT
func reflect·typelinks() (ret Slice) {
extern Type *typelink[], *etypelink[];
ret.array = (byte*)typelink;
ret.len = etypelink - typelink;
ret.cap = ret.len;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment