github.com/primecitizens/pcz/std@v0.2.1/runtime/builtin_interface.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  //go:build pcz
     5  
     6  package runtime
     7  
     8  import (
     9  	"unsafe"
    10  
    11  	stdconst "github.com/primecitizens/pcz/std/builtin/const"
    12  	stdtype "github.com/primecitizens/pcz/std/builtin/type"
    13  	"github.com/primecitizens/pcz/std/core/abi"
    14  	"github.com/primecitizens/pcz/std/core/arch"
    15  	"github.com/primecitizens/pcz/std/core/asan"
    16  	"github.com/primecitizens/pcz/std/core/assert"
    17  	"github.com/primecitizens/pcz/std/core/mem"
    18  	"github.com/primecitizens/pcz/std/core/msan"
    19  	"github.com/primecitizens/pcz/std/core/race"
    20  )
    21  
    22  //
    23  // interface type operations
    24  //
    25  
    26  // Convert non-interface type to the data word of a (empty or nonempty) interface.
    27  func convT(typ *abi.Type, elem unsafe.Pointer) unsafe.Pointer {
    28  	if race.Enabled {
    29  		race.ReadObjectPC(typ, elem, getcallerpc(), abi.FuncPCABIInternal(convT))
    30  	}
    31  	if msan.Enabled {
    32  		msan.Read(elem, typ.Size_)
    33  	}
    34  	if asan.Enabled {
    35  		asan.Read(elem, typ.Size_)
    36  	}
    37  
    38  	x := mallocgc(typ.Size_, typ, true)
    39  	mem.TypedMove(typ, x, elem)
    40  	return x
    41  }
    42  
    43  // Same as convT, for types with no pointers in them.
    44  func convTnoptr(typ *abi.Type, elem unsafe.Pointer) unsafe.Pointer {
    45  	// TODO: maybe take size instead of type?
    46  	if race.Enabled {
    47  		race.ReadObjectPC(typ, elem, getcallerpc(), abi.FuncPCABIInternal(convTnoptr))
    48  	}
    49  	if msan.Enabled {
    50  		msan.Read(elem, typ.Size_)
    51  	}
    52  	if asan.Enabled {
    53  		asan.Read(elem, typ.Size_)
    54  	}
    55  
    56  	x := mallocgc(typ.Size_, typ, false)
    57  	mem.Move(x, elem, typ.Size_)
    58  	return x
    59  }
    60  
    61  // Specialized versions of convT for specific types.
    62  // These functions take concrete types in the runtime. But they may
    63  // be used for a wider range of types, which have the same memory
    64  // layout as the parameter type. The compiler converts the
    65  // to-be-converted type to the parameter type before calling the
    66  // runtime function. This way, the call is ABI-insensitive.
    67  
    68  // staticuint64s is used to avoid allocating in convTx for small integer values.
    69  var staticuint64s = [...]uint64{
    70  	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    71  	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    72  	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    73  	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    74  	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    75  	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    76  	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    77  	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    78  	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    79  	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    80  	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
    81  	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
    82  	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
    83  	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    84  	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
    85  	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
    86  	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    87  	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    88  	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    89  	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    90  	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    91  	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    92  	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    93  	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    94  	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    95  	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    96  	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    97  	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    98  	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    99  	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
   100  	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
   101  	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
   102  }
   103  
   104  func elemTypeOf(ptr any) *abi.Type {
   105  	return stdtype.TypeOf(ptr).PointerTypeUnsafe().Elem
   106  }
   107  
   108  func convT16(val uint16) (x unsafe.Pointer) {
   109  	if val < uint16(len(staticuint64s)) {
   110  		if arch.BigEndian {
   111  			return unsafe.Pointer(uintptr(x) + 6)
   112  		}
   113  		return unsafe.Pointer(&staticuint64s[val])
   114  	}
   115  
   116  	x = mallocgc(stdconst.SizeUint16Type, elemTypeOf((*uint16)(nil)), false)
   117  	*(*uint16)(x) = val
   118  	return
   119  }
   120  
   121  func convT32(val uint32) (x unsafe.Pointer) {
   122  	if val < uint32(len(staticuint64s)) {
   123  		if arch.BigEndian {
   124  			return unsafe.Pointer(uintptr(x) + 4)
   125  		}
   126  		return unsafe.Pointer(&staticuint64s[val])
   127  	}
   128  
   129  	x = mallocgc(stdconst.SizeUint32Type, elemTypeOf((*uint32)(nil)), false)
   130  	*(*uint32)(x) = val
   131  	return
   132  }
   133  
   134  func convT64(val uint64) unsafe.Pointer {
   135  	if val < uint64(len(staticuint64s)) {
   136  		return unsafe.Pointer(&staticuint64s[val])
   137  	}
   138  
   139  	x := mallocgc(stdconst.SizeUint64Type, elemTypeOf((*uint64)(nil)), false)
   140  	*(*uint64)(x) = val
   141  	return x
   142  }
   143  
   144  var (
   145  	zeroVal [max(stdconst.SizeSliceType, stdconst.SizeStringType)]byte
   146  )
   147  
   148  func convTstring(val string) unsafe.Pointer {
   149  	if len(val) == 0 {
   150  		return unsafe.Pointer(&zeroVal[0])
   151  	}
   152  
   153  	x := mallocgc(stdconst.SizeStringType, elemTypeOf((*string)(nil)), true)
   154  	*(*string)(x) = val
   155  	return x
   156  }
   157  
   158  func convTslice(val []byte) unsafe.Pointer {
   159  	// Note: this must work for any element type, not just byte.
   160  	if unsafe.SliceData(val) == nil {
   161  		return unsafe.Pointer(&zeroVal[0])
   162  	}
   163  
   164  	x := mallocgc(stdconst.SizeSliceType, elemTypeOf((*[]byte)(nil)), true)
   165  	*(*[]byte)(x) = val
   166  	return x
   167  }
   168  
   169  // panicdottypeE is called when doing an e.(T) conversion and the conversion fails.
   170  // have = the dynamic type we have.
   171  // want = the static type we're trying to convert to.
   172  // iface = the static type we're converting from.
   173  func panicdottypeE(have, want, iface *abi.Type) {
   174  	assert.Panic(stdtype.TypeAssertionError{
   175  		Interface:     iface,
   176  		Concrete:      have,
   177  		Asserted:      want,
   178  		MissingMethod: "",
   179  	})
   180  }
   181  
   182  // panicdottypeI is called when doing an i.(T) conversion and the conversion fails.
   183  // Same args as panicdottypeE, but "have" is the dynamic itab we have.
   184  func panicdottypeI(have *abi.Itab, want, iface *abi.Type) {
   185  	var t *abi.Type
   186  	if have != nil {
   187  		t = have.Type
   188  	}
   189  	panicdottypeE(t, want, iface)
   190  }
   191  
   192  // panicnildottype is called when doing a i.(T) conversion and the interface i is nil.
   193  // want = the static type we're trying to convert to.
   194  func panicnildottype(want *abi.Type) {
   195  	assert.Panic(stdtype.TypeAssertionError{
   196  		Interface:     nil,
   197  		Concrete:      nil,
   198  		Asserted:      want,
   199  		MissingMethod: "",
   200  	})
   201  	// TODO: Add the static type we're converting from as well.
   202  	// It might generate a better error message.
   203  	// Just to match other nil conversion errors, we don't for now.
   204  }
   205  
   206  func getitab(inter *abi.InterfaceType, typ *abi.Type, canfail bool) (itab *abi.Itab) {
   207  	itab, code := abi.GetItab(inter, typ)
   208  	switch code {
   209  	case abi.GetItabResult_OK:
   210  		return itab
   211  	case abi.GetItabResult_UncommonType:
   212  		if canfail {
   213  			return nil
   214  		}
   215  		name := inter.Type.NameOff(inter.Methods[0].Name)
   216  		assert.Panic(stdtype.TypeAssertionError{
   217  			Interface:     nil,
   218  			Concrete:      typ,
   219  			Asserted:      &inter.Type,
   220  			MissingMethod: name.Name(),
   221  		})
   222  		return nil
   223  	case abi.GetItabResult_:
   224  		if canfail {
   225  			return nil
   226  		}
   227  		assert.Panic(stdtype.TypeAssertionError{
   228  			Concrete:      typ,
   229  			Asserted:      &inter.Type,
   230  			MissingMethod: itab.Init(),
   231  		})
   232  		return nil
   233  	default:
   234  		assert.Unreachable()
   235  		return nil
   236  	}
   237  }
   238  
   239  //
   240  // Non-empty-interface to non-empty-interface conversion.
   241  //
   242  
   243  // convI2I returns the new itab to be used for the destination value
   244  // when converting a value with itab src to the dst interface.
   245  func convI2I(dst *abi.InterfaceType, src *abi.Itab) *abi.Itab {
   246  	if src == nil {
   247  		return nil
   248  	}
   249  	if src.Inter == dst {
   250  		return src
   251  	}
   252  
   253  	return getitab(dst, src.Type, false)
   254  }
   255  
   256  // interface type assertions x.(T)
   257  
   258  func assertI2I(inter *abi.InterfaceType, tab *abi.Itab) *abi.Itab {
   259  	if tab == nil {
   260  		// explicit conversions require non-nil interface value.
   261  		assert.Panic(stdtype.TypeAssertionError{
   262  			Interface:     nil,
   263  			Concrete:      nil,
   264  			Asserted:      &inter.Type,
   265  			MissingMethod: "",
   266  		})
   267  	}
   268  	if tab.Inter == inter {
   269  		return tab
   270  	}
   271  	return getitab(inter, tab.Type, false)
   272  }
   273  
   274  func assertI2I2(inter *abi.InterfaceType, i stdtype.Iface) (r stdtype.Iface) {
   275  	tab := i.Itab
   276  	if tab == nil {
   277  		return
   278  	}
   279  	if tab.Inter != inter {
   280  		tab = getitab(inter, tab.Type, true)
   281  		if tab == nil {
   282  			return
   283  		}
   284  	}
   285  	r.Itab = tab
   286  	r.Data = i.Data
   287  	return
   288  }
   289  
   290  func assertE2I(inter *abi.InterfaceType, t *abi.Type) *abi.Itab {
   291  	if t == nil {
   292  		// explicit conversions require non-nil interface value.
   293  		assert.Panic(stdtype.TypeAssertionError{
   294  			Interface:     nil,
   295  			Concrete:      nil,
   296  			Asserted:      &inter.Type,
   297  			MissingMethod: "",
   298  		})
   299  	}
   300  	return getitab(inter, t, false)
   301  }
   302  
   303  func assertE2I2(inter *abi.InterfaceType, e stdtype.Eface) (r stdtype.Iface) {
   304  	t := e.Type
   305  	if t == nil {
   306  		return
   307  	}
   308  	tab := getitab(inter, t, true)
   309  	if tab == nil {
   310  		return
   311  	}
   312  	r.Itab = tab
   313  	r.Data = e.Data
   314  	return
   315  }
   316  
   317  // interface equality. Type/itab pointers are already known to be equal, so
   318  // we only need to pass one.
   319  
   320  func ifaceeq(tab *abi.Itab, x, y unsafe.Pointer) bool {
   321  	if tab == nil {
   322  		return true
   323  	}
   324  	t := tab.Type
   325  	eq := t.Equal
   326  	if eq == nil {
   327  		assert.Panic("comparing", "uncomparable", "type ", t.String())
   328  	}
   329  	if t.IsDirectIface() {
   330  		// See comment in EfaceEq.
   331  		return x == y
   332  	}
   333  	return eq(x, y)
   334  }
   335  
   336  func efaceeq(t *abi.Type, x, y unsafe.Pointer) bool {
   337  	if t == nil {
   338  		return true
   339  	}
   340  	eq := t.Equal
   341  	if eq == nil {
   342  		assert.Panic("comparing", "uncomparable", "type ", t.String())
   343  	}
   344  	if t.IsDirectIface() {
   345  		// Direct interface types are ptr, chan, map, func, and single-element structs/arrays thereof.
   346  		// Maps and funcs are not comparable, so they can't reach here.
   347  		// Ptrs, chans, and single-element items can be compared directly using ==.
   348  		return x == y
   349  	}
   350  	return eq(x, y)
   351  }