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 }