From cd3a4bb8c8be3b92f1be96f7c2abcd11a08c4b6d Mon Sep 17 00:00:00 2001 From: visualfc Date: Tue, 26 Nov 2024 20:45:01 +0800 Subject: [PATCH 1/4] reflect: implement map --- internal/lib/reflect/type.go | 169 ++++++++++ internal/lib/reflect/value.go | 599 ++++++++++++++++++---------------- 2 files changed, 480 insertions(+), 288 deletions(-) diff --git a/internal/lib/reflect/type.go b/internal/lib/reflect/type.go index e030d4f7..4b6f4410 100644 --- a/internal/lib/reflect/type.go +++ b/internal/lib/reflect/type.go @@ -27,6 +27,7 @@ import ( "github.com/goplus/llgo/internal/abi" "github.com/goplus/llgo/internal/lib/sync" "github.com/goplus/llgo/internal/runtime" + "github.com/goplus/llgo/internal/runtime/goarch" ) // Type is the representation of a Go type. @@ -1466,3 +1467,171 @@ func funcStr(ft *funcType) string { } return string(repr) } + +// isReflexive reports whether the == operation on the type is reflexive. +// That is, x == x for all values x of type t. +func isReflexive(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, String, UnsafePointer: + return true + case Float32, Float64, Complex64, Complex128, Interface: + return false + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return isReflexive(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if !isReflexive(f.Typ) { + return false + } + } + return true + default: + // Func, Map, Slice, Invalid + panic("isReflexive called on non-key type " + stringFor(t)) + } +} + +// needKeyUpdate reports whether map overwrites require the key to be copied. +func needKeyUpdate(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Chan, Pointer, UnsafePointer: + return false + case Float32, Float64, Complex64, Complex128, Interface, String: + // Float keys can be updated from +0 to -0. + // String keys can be updated to use a smaller backing store. + // Interfaces might have floats of strings in them. + return true + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return needKeyUpdate(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if needKeyUpdate(f.Typ) { + return true + } + } + return false + default: + // Func, Map, Slice, Invalid + panic("needKeyUpdate called on non-key type " + stringFor(t)) + } +} + +// hashMightPanic reports whether the hash of a map key of type t might panic. +func hashMightPanic(t *abi.Type) bool { + switch Kind(t.Kind()) { + case Interface: + return true + case Array: + tt := (*arrayType)(unsafe.Pointer(t)) + return hashMightPanic(tt.Elem) + case Struct: + tt := (*structType)(unsafe.Pointer(t)) + for _, f := range tt.Fields { + if hashMightPanic(f.Typ) { + return true + } + } + return false + default: + return false + } +} + +// Make sure these routines stay in sync with ../runtime/map.go! +// These types exist only for GC, so we only fill out GC relevant info. +// Currently, that's just size and the GC program. We also fill in string +// for possible debugging use. +const ( + bucketSize uintptr = abi.MapBucketCount + maxKeySize uintptr = abi.MapMaxKeyBytes + maxValSize uintptr = abi.MapMaxElemBytes +) + +func bucketOf(ktyp, etyp *abi.Type) *abi.Type { + if ktyp.Size_ > maxKeySize { + ktyp = ptrTo(ktyp) + } + if etyp.Size_ > maxValSize { + etyp = ptrTo(etyp) + } + + // Prepare GC data if any. + // A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+ptrSize bytes, + // or 2064 bytes, or 258 pointer-size words, or 33 bytes of pointer bitmap. + // Note that since the key and value are known to be <= 128 bytes, + // they're guaranteed to have bitmaps instead of GC programs. + var gcdata *byte + var ptrdata uintptr + + size := bucketSize*(1+ktyp.Size_+etyp.Size_) + goarch.PtrSize + if size&uintptr(ktyp.Align_-1) != 0 || size&uintptr(etyp.Align_-1) != 0 { + panic("reflect: bad size computation in MapOf") + } + + if ktyp.PtrBytes != 0 || etyp.PtrBytes != 0 { + nptr := (bucketSize*(1+ktyp.Size_+etyp.Size_) + goarch.PtrSize) / goarch.PtrSize + n := (nptr + 7) / 8 + + // Runtime needs pointer masks to be a multiple of uintptr in size. + n = (n + goarch.PtrSize - 1) &^ (goarch.PtrSize - 1) + mask := make([]byte, n) + base := bucketSize / goarch.PtrSize + + if ktyp.PtrBytes != 0 { + emitGCMask(mask, base, ktyp, bucketSize) + } + base += bucketSize * ktyp.Size_ / goarch.PtrSize + + if etyp.PtrBytes != 0 { + emitGCMask(mask, base, etyp, bucketSize) + } + base += bucketSize * etyp.Size_ / goarch.PtrSize + + word := base + mask[word/8] |= 1 << (word % 8) + gcdata = &mask[0] + ptrdata = (word + 1) * goarch.PtrSize + + // overflow word must be last + if ptrdata != size { + panic("reflect: bad layout computation in MapOf") + } + } + + b := &abi.Type{ + Align_: goarch.PtrSize, + Size_: size, + Kind_: uint8(Struct), + PtrBytes: ptrdata, + GCData: gcdata, + } + b.Str_ = "bucket(" + stringFor(ktyp) + "," + stringFor(etyp) + ")" + return b +} + +func (t *rtype) gcSlice(begin, end uintptr) []byte { + return (*[1 << 30]byte)(unsafe.Pointer(t.t.GCData))[begin:end:end] +} + +// emitGCMask writes the GC mask for [n]typ into out, starting at bit +// offset base. +func emitGCMask(out []byte, base uintptr, typ *abi.Type, n uintptr) { + // if typ.Kind_&kindGCProg != 0 { + // panic("reflect: unexpected GC program") + // } + // ptrs := typ.PtrBytes / goarch.PtrSize + // words := typ.Size_ / goarch.PtrSize + // mask := typ.GcSlice(0, (ptrs+7)/8) + // for j := uintptr(0); j < ptrs; j++ { + // if (mask[j/8]>>(j%8))&1 != 0 { + // for i := uintptr(0); i < n; i++ { + // k := base + i*words + j + // out[k/8] |= 1 << (k % 8) + // } + // } + // } +} diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go index ca484402..60800460 100644 --- a/internal/lib/reflect/value.go +++ b/internal/lib/reflect/value.go @@ -1251,29 +1251,28 @@ func (v Value) SetCap(n int) { // As in Go, key's elem must be assignable to the map's key type, // and elem's value must be assignable to the map's elem type. func (v Value) SetMapIndex(key, elem Value) { - /* TODO(xsw): v.mustBe(Map) v.mustBeExported() key.mustBeExported() tt := (*mapType)(unsafe.Pointer(v.typ())) - if (tt.Key == stringType || key.kind() == String) && tt.Key == key.typ() && tt.Elem.Size() <= maxValSize { - k := *(*string)(key.ptr) - if elem.typ() == nil { - mapdelete_faststr(v.typ(), v.pointer(), k) - return - } - elem.mustBeExported() - elem = elem.assignTo("reflect.Value.SetMapIndex", tt.Elem, nil) - var e unsafe.Pointer - if elem.flag&flagIndir != 0 { - e = elem.ptr - } else { - e = unsafe.Pointer(&elem.ptr) - } - mapassign_faststr(v.typ(), v.pointer(), k, e) - return - } + // if (tt.Key == stringType || key.kind() == String) && tt.Key == key.typ() && tt.Elem.Size() <= maxValSize { + // k := *(*string)(key.ptr) + // if elem.typ() == nil { + // mapdelete_faststr(v.typ(), v.pointer(), k) + // return + // } + // elem.mustBeExported() + // elem = elem.assignTo("reflect.Value.SetMapIndex", tt.Elem, nil) + // var e unsafe.Pointer + // if elem.flag&flagIndir != 0 { + // e = elem.ptr + // } else { + // e = unsafe.Pointer(&elem.ptr) + // } + // mapassign_faststr(v.typ(), v.pointer(), k, e) + // return + // } key = key.assignTo("reflect.Value.SetMapIndex", tt.Key, nil) var k unsafe.Pointer @@ -1295,8 +1294,6 @@ func (v Value) SetMapIndex(key, elem Value) { e = unsafe.Pointer(&elem.ptr) } mapassign(v.typ(), v.pointer(), k, e) - */ - panic("todo: reflect.Value.SetMapIndex") } // SetUint sets v's underlying value to x. @@ -2270,296 +2267,277 @@ func (v Value) call(op string, in []Value) (out []Value) { return } -// var callGC bool // for testing; see TestCallMethodJump and TestCallArgLive +var stringType = rtypeOf("") -// const debugReflectCall = false +// MapIndex returns the value associated with key in the map v. +// It panics if v's Kind is not Map. +// It returns the zero Value if key is not found in the map or if v represents a nil map. +// As in Go, the key's value must be assignable to the map's key type. +func (v Value) MapIndex(key Value) Value { + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ())) -// func (v Value) call(op string, in []Value) []Value { -// // Get function pointer, type. -// t := (*funcType)(unsafe.Pointer(v.typ())) -// var ( -// fn unsafe.Pointer -// rcvr Value -// rcvrtype *abi.Type -// ) -// if v.flag&flagMethod != 0 { -// rcvr = v -// rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift) -// } else if v.flag&flagIndir != 0 { -// fn = *(*unsafe.Pointer)(v.ptr) -// } else { -// fn = v.ptr -// } + // Do not require key to be exported, so that DeepEqual + // and other programs can use all the keys returned by + // MapKeys as arguments to MapIndex. If either the map + // or the key is unexported, though, the result will be + // considered unexported. This is consistent with the + // behavior for structs, which allow read but not write + // of unexported fields. -// if fn == nil { -// panic("reflect.Value.Call: call of nil function") -// } + var e unsafe.Pointer + // if (tt.Key == stringType || key.kind() == String) && tt.Key == key.typ() && tt.Elem.Size() <= maxValSize { + // k := *(*string)(key.ptr) + // e = mapaccess_faststr(v.typ(), v.pointer(), k) + // } else { + key = key.assignTo("reflect.Value.MapIndex", tt.Key, nil) + var k unsafe.Pointer + if key.flag&flagIndir != 0 { + k = key.ptr + } else { + k = unsafe.Pointer(&key.ptr) + } + e = mapaccess(v.typ(), v.pointer(), k) + // } + if e == nil { + return Value{} + } + typ := tt.Elem + fl := (v.flag | key.flag).ro() + fl |= flag(typ.Kind()) + return copyVal(typ, fl, e) +} -// isSlice := op == "CallSlice" -// n := t.NumIn() -// isVariadic := t.IsVariadic() -// if isSlice { -// if !isVariadic { -// panic("reflect: CallSlice of non-variadic function") -// } -// if len(in) < n { -// panic("reflect: CallSlice with too few input arguments") -// } -// if len(in) > n { -// panic("reflect: CallSlice with too many input arguments") -// } -// } else { -// if isVariadic { -// n-- -// } -// if len(in) < n { -// panic("reflect: Call with too few input arguments") -// } -// if !isVariadic && len(in) > n { -// panic("reflect: Call with too many input arguments") -// } -// } -// for _, x := range in { -// if x.Kind() == Invalid { -// panic("reflect: " + op + " using zero Value argument") -// } -// } -// for i := 0; i < n; i++ { -// if xt, targ := in[i].Type(), t.In(i); !xt.AssignableTo(toRType(targ)) { -// panic("reflect: " + op + " using " + xt.String() + " as type " + stringFor(targ)) -// } -// } -// if !isSlice && isVariadic { -// // prepare slice for remaining values -// m := len(in) - n -// slice := MakeSlice(toRType(t.In(n)), m, m) -// elem := toRType(t.In(n)).Elem() // FIXME cast to slice type and Elem() -// for i := 0; i < m; i++ { -// x := in[n+i] -// if xt := x.Type(); !xt.AssignableTo(elem) { -// panic("reflect: cannot use " + xt.String() + " as type " + elem.String() + " in " + op) -// } -// slice.Index(i).Set(x) -// } -// origIn := in -// in = make([]Value, n+1) -// copy(in[:n], origIn) -// in[n] = slice -// } +// MapKeys returns a slice containing all the keys present in the map, +// in unspecified order. +// It panics if v's Kind is not Map. +// It returns an empty slice if v represents a nil map. +func (v Value) MapKeys() []Value { + v.mustBe(Map) + tt := (*mapType)(unsafe.Pointer(v.typ())) + keyType := tt.Key -// nin := len(in) -// if nin != t.NumIn() { -// panic("reflect.Value.Call: wrong argument count") -// } -// nout := t.NumOut() + fl := v.flag.ro() | flag(keyType.Kind()) -// // Register argument space. -// var regArgs abi.RegArgs + m := v.pointer() + mlen := int(0) + if m != nil { + mlen = maplen(m) + } + var it hiter + mapiterinit(v.typ(), m, &it) + a := make([]Value, mlen) + var i int + for i = 0; i < len(a); i++ { + key := mapiterkey(&it) + if key == nil { + // Someone deleted an entry from the map since we + // called maplen above. It's a data race, but nothing + // we can do about it. + break + } + a[i] = copyVal(keyType, fl, key) + mapiternext(&it) + } + return a[:i] +} -// // Compute frame type. -// frametype, framePool, abid := funcLayout(t, rcvrtype) +// hiter's structure matches runtime.hiter's structure. +// Having a clone here allows us to embed a map iterator +// inside type MapIter so that MapIters can be re-used +// without doing any allocations. +type hiter struct { + key unsafe.Pointer + elem unsafe.Pointer + t unsafe.Pointer + h unsafe.Pointer + buckets unsafe.Pointer + bptr unsafe.Pointer + overflow *[]unsafe.Pointer + oldoverflow *[]unsafe.Pointer + startBucket uintptr + offset uint8 + wrapped bool + B uint8 + i uint8 + bucket uintptr + checkBucket uintptr +} -// // Allocate a chunk of memory for frame if needed. -// var stackArgs unsafe.Pointer -// if frametype.Size() != 0 { -// if nout == 0 { -// stackArgs = framePool.Get().(unsafe.Pointer) -// } else { -// // Can't use pool if the function has return values. -// // We will leak pointer to args in ret, so its lifetime is not scoped. -// stackArgs = unsafe_New(frametype) -// } -// } -// frameSize := frametype.Size() +func (h *hiter) initialized() bool { + return h.t != nil +} -// if debugReflectCall { -// println("reflect.call", stringFor(&t.Type)) -// abid.dump() -// } +// A MapIter is an iterator for ranging over a map. +// See Value.MapRange. +type MapIter struct { + m Value + hiter hiter +} -// // Copy inputs into args. +// Key returns the key of iter's current map entry. +func (iter *MapIter) Key() Value { + if !iter.hiter.initialized() { + panic("MapIter.Key called before Next") + } + iterkey := mapiterkey(&iter.hiter) + if iterkey == nil { + panic("MapIter.Key called on exhausted iterator") + } -// // Handle receiver. -// inStart := 0 -// if rcvrtype != nil { -// // Guaranteed to only be one word in size, -// // so it will only take up exactly 1 abiStep (either -// // in a register or on the stack). -// switch st := abid.call.steps[0]; st.kind { -// case abiStepStack: -// storeRcvr(rcvr, stackArgs) -// case abiStepPointer: -// storeRcvr(rcvr, unsafe.Pointer(®Args.Ptrs[st.ireg])) -// fallthrough -// case abiStepIntReg: -// storeRcvr(rcvr, unsafe.Pointer(®Args.Ints[st.ireg])) -// case abiStepFloatReg: -// storeRcvr(rcvr, unsafe.Pointer(®Args.Floats[st.freg])) -// default: -// panic("unknown ABI parameter kind") -// } -// inStart = 1 -// } + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + ktype := t.Key + return copyVal(ktype, iter.m.flag.ro()|flag(ktype.Kind()), iterkey) +} -// // Handle arguments. -// for i, v := range in { -// v.mustBeExported() -// targ := toRType(t.In(i)) -// // TODO(mknyszek): Figure out if it's possible to get some -// // scratch space for this assignment check. Previously, it -// // was possible to use space in the argument frame. -// v = v.assignTo("reflect.Value.Call", &targ.t, nil) -// stepsLoop: -// for _, st := range abid.call.stepsForValue(i + inStart) { -// switch st.kind { -// case abiStepStack: -// // Copy values to the "stack." -// addr := add(stackArgs, st.stkOff, "precomputed stack arg offset") -// if v.flag&flagIndir != 0 { -// typedmemmove(&targ.t, addr, v.ptr) -// } else { -// *(*unsafe.Pointer)(addr) = v.ptr -// } -// // There's only one step for a stack-allocated value. -// break stepsLoop -// case abiStepIntReg, abiStepPointer: -// // Copy values to "integer registers." -// if v.flag&flagIndir != 0 { -// offset := add(v.ptr, st.offset, "precomputed value offset") -// if st.kind == abiStepPointer { -// // Duplicate this pointer in the pointer area of the -// // register space. Otherwise, there's the potential for -// // this to be the last reference to v.ptr. -// regArgs.Ptrs[st.ireg] = *(*unsafe.Pointer)(offset) -// } -// intToReg(®Args, st.ireg, st.size, offset) -// } else { -// if st.kind == abiStepPointer { -// // See the comment in abiStepPointer case above. -// regArgs.Ptrs[st.ireg] = v.ptr -// } -// regArgs.Ints[st.ireg] = uintptr(v.ptr) -// } -// case abiStepFloatReg: -// // Copy values to "float registers." -// if v.flag&flagIndir == 0 { -// panic("attempted to copy pointer to FP register") -// } -// offset := add(v.ptr, st.offset, "precomputed value offset") -// floatToReg(®Args, st.freg, st.size, offset) -// default: -// panic("unknown ABI part kind") -// } -// } -// } -// // TODO(mknyszek): Remove this when we no longer have -// // caller reserved spill space. -// frameSize = align(frameSize, goarch.PtrSize) -// frameSize += abid.spill +// SetIterKey assigns to v the key of iter's current map entry. +// It is equivalent to v.Set(iter.Key()), but it avoids allocating a new Value. +// As in Go, the key must be assignable to v's type and +// must not be derived from an unexported field. +func (v Value) SetIterKey(iter *MapIter) { + if !iter.hiter.initialized() { + panic("reflect: Value.SetIterKey called before Next") + } + iterkey := mapiterkey(&iter.hiter) + if iterkey == nil { + panic("reflect: Value.SetIterKey called on exhausted iterator") + } -// // Mark pointers in registers for the return path. -// regArgs.ReturnIsPtr = abid.outRegPtrs + v.mustBeAssignable() + var target unsafe.Pointer + if v.kind() == Interface { + target = v.ptr + } -// if debugReflectCall { -// regArgs.Dump() -// } + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + ktype := t.Key -// // For testing; see TestCallArgLive. -// if callGC { -// runtime.GC() -// } + iter.m.mustBeExported() // do not let unexported m leak + key := Value{ktype, iterkey, iter.m.flag | flag(ktype.Kind()) | flagIndir} + key = key.assignTo("reflect.MapIter.SetKey", v.typ(), target) + typedmemmove(v.typ(), v.ptr, key.ptr) +} -// // Call. -// call(frametype, fn, stackArgs, uint32(frametype.Size()), uint32(abid.retOffset), uint32(frameSize), ®Args) +// Value returns the value of iter's current map entry. +func (iter *MapIter) Value() Value { + if !iter.hiter.initialized() { + panic("MapIter.Value called before Next") + } + iterelem := mapiterelem(&iter.hiter) + if iterelem == nil { + panic("MapIter.Value called on exhausted iterator") + } -// // For testing; see TestCallMethodJump. -// if callGC { -// runtime.GC() -// } + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + vtype := t.Elem + return copyVal(vtype, iter.m.flag.ro()|flag(vtype.Kind()), iterelem) +} -// var ret []Value -// if nout == 0 { -// if stackArgs != nil { -// typedmemclr(frametype, stackArgs) -// framePool.Put(stackArgs) -// } -// } else { -// if stackArgs != nil { -// // Zero the now unused input area of args, -// // because the Values returned by this function contain pointers to the args object, -// // and will thus keep the args object alive indefinitely. -// typedmemclrpartial(frametype, stackArgs, 0, abid.retOffset) -// } +// SetIterValue assigns to v the value of iter's current map entry. +// It is equivalent to v.Set(iter.Value()), but it avoids allocating a new Value. +// As in Go, the value must be assignable to v's type and +// must not be derived from an unexported field. +func (v Value) SetIterValue(iter *MapIter) { + if !iter.hiter.initialized() { + panic("reflect: Value.SetIterValue called before Next") + } + iterelem := mapiterelem(&iter.hiter) + if iterelem == nil { + panic("reflect: Value.SetIterValue called on exhausted iterator") + } -// // Wrap Values around return values in args. -// ret = make([]Value, nout) -// for i := 0; i < nout; i++ { -// tv := t.Out(i) -// if tv.Size() == 0 { -// // For zero-sized return value, args+off may point to the next object. -// // In this case, return the zero value instead. -// ret[i] = Zero(toRType(tv)) -// continue -// } -// steps := abid.ret.stepsForValue(i) -// if st := steps[0]; st.kind == abiStepStack { -// // This value is on the stack. If part of a value is stack -// // allocated, the entire value is according to the ABI. So -// // just make an indirection into the allocated frame. -// fl := flagIndir | flag(tv.Kind()) -// ret[i] = Value{tv, add(stackArgs, st.stkOff, "tv.Size() != 0"), fl} -// // Note: this does introduce false sharing between results - -// // if any result is live, they are all live. -// // (And the space for the args is live as well, but as we've -// // cleared that space it isn't as big a deal.) -// continue -// } + v.mustBeAssignable() + var target unsafe.Pointer + if v.kind() == Interface { + target = v.ptr + } -// // Handle pointers passed in registers. -// if !ifaceIndir(tv) { -// // Pointer-valued data gets put directly -// // into v.ptr. -// if steps[0].kind != abiStepPointer { -// print("kind=", steps[0].kind, ", type=", stringFor(tv), "\n") -// panic("mismatch between ABI description and types") -// } -// ret[i] = Value{tv, regArgs.Ptrs[steps[0].ireg], flag(tv.Kind())} -// continue -// } + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + vtype := t.Elem -// // All that's left is values passed in registers that we need to -// // create space for and copy values back into. -// // -// // TODO(mknyszek): We make a new allocation for each register-allocated -// // value, but previously we could always point into the heap-allocated -// // stack frame. This is a regression that could be fixed by adding -// // additional space to the allocated stack frame and storing the -// // register-allocated return values into the allocated stack frame and -// // referring there in the resulting Value. -// s := unsafe_New(tv) -// for _, st := range steps { -// switch st.kind { -// case abiStepIntReg: -// offset := add(s, st.offset, "precomputed value offset") -// intFromReg(®Args, st.ireg, st.size, offset) -// case abiStepPointer: -// s := add(s, st.offset, "precomputed value offset") -// *((*unsafe.Pointer)(s)) = regArgs.Ptrs[st.ireg] -// case abiStepFloatReg: -// offset := add(s, st.offset, "precomputed value offset") -// floatFromReg(®Args, st.freg, st.size, offset) -// case abiStepStack: -// panic("register-based return value has stack component") -// default: -// panic("unknown ABI part kind") -// } -// } -// ret[i] = Value{tv, s, flagIndir | flag(tv.Kind())} -// } -// } + iter.m.mustBeExported() // do not let unexported m leak + elem := Value{vtype, iterelem, iter.m.flag | flag(vtype.Kind()) | flagIndir} + elem = elem.assignTo("reflect.MapIter.SetValue", v.typ(), target) + typedmemmove(v.typ(), v.ptr, elem.ptr) +} -// return ret -// } +// Next advances the map iterator and reports whether there is another +// entry. It returns false when iter is exhausted; subsequent +// calls to Key, Value, or Next will panic. +func (iter *MapIter) Next() bool { + if !iter.m.IsValid() { + panic("MapIter.Next called on an iterator that does not have an associated map Value") + } + if !iter.hiter.initialized() { + mapiterinit(iter.m.typ(), iter.m.pointer(), &iter.hiter) + } else { + if mapiterkey(&iter.hiter) == nil { + panic("MapIter.Next called on exhausted iterator") + } + mapiternext(&iter.hiter) + } + return mapiterkey(&iter.hiter) != nil +} + +// Reset modifies iter to iterate over v. +// It panics if v's Kind is not Map and v is not the zero Value. +// Reset(Value{}) causes iter to not to refer to any map, +// which may allow the previously iterated-over map to be garbage collected. +func (iter *MapIter) Reset(v Value) { + if v.IsValid() { + v.mustBe(Map) + } + iter.m = v + iter.hiter = hiter{} +} + +// MapRange returns a range iterator for a map. +// It panics if v's Kind is not Map. +// +// Call Next to advance the iterator, and Key/Value to access each entry. +// Next returns false when the iterator is exhausted. +// MapRange follows the same iteration semantics as a range statement. +// +// Example: +// +// iter := reflect.ValueOf(m).MapRange() +// for iter.Next() { +// k := iter.Key() +// v := iter.Value() +// ... +// } +func (v Value) MapRange() *MapIter { + // This is inlinable to take advantage of "function outlining". + // The allocation of MapIter can be stack allocated if the caller + // does not allow it to escape. + // See https://blog.filippo.io/efficient-go-apis-with-the-inliner/ + if v.kind() != Map { + v.panicNotMap() + } + return &MapIter{m: v} +} + +// Force slow panicking path not inlined, so it won't add to the +// inlining budget of the caller. +// TODO: undo when the inliner is no longer bottom-up only. +// +//go:noinline +func (f flag) panicNotMap() { + f.mustBe(Map) +} + +// copyVal returns a Value containing the map key or value at ptr, +// allocating a new variable as needed. +func copyVal(typ *abi.Type, fl flag, ptr unsafe.Pointer) Value { + if typ.IfaceIndir() { + // Copy result so future changes to the map + // won't change the underlying value. + c := unsafe_New(typ) + typedmemmove(typ, c, ptr) + return Value{typ, c, fl | flagIndir} + } + return Value{typ, *(*unsafe.Pointer)(ptr), fl} +} // methodReceiver returns information about the receiver // described by v. The Value v may or may not have the @@ -2612,6 +2590,51 @@ func chanlen(ch unsafe.Pointer) int //go:linkname maplen github.com/goplus/llgo/internal/runtime.MapLen func maplen(ch unsafe.Pointer) int +//go:linkname mapaccess github.com/goplus/llgo/internal/runtime.MapAccess1 +func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) + +//go:linkname mapassign0 github.com/goplus/llgo/internal/runtime.MapAssign +func mapassign0(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer + +func mapassign(t *abi.Type, m unsafe.Pointer, key, val unsafe.Pointer) { + contentEscapes(key) + contentEscapes(val) + p := mapassign0(t, m, key) + runtime.Typedmemmove(t.Elem(), p, val) +} + +// //go:noescape +// func mapassign_faststr0(t *abi.Type, m unsafe.Pointer, key string, val unsafe.Pointer) + +// func mapassign_faststr(t *abi.Type, m unsafe.Pointer, key string, val unsafe.Pointer) { +// contentEscapes((*unsafeheader.String)(unsafe.Pointer(&key)).Data) +// contentEscapes(val) +// mapassign_faststr0(t, m, key, val) +// } + +//go:linkname mapdelete github.com/goplus/llgo/internal/runtime.MapDelete +func mapdelete(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) + +//go:noescape +// func mapdelete_faststr(t *abi.Type, m unsafe.Pointer, key string) + +//go:linkname mapiterinit github.com/goplus/llgo/internal/runtime.mapiterinit +func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter) + +func mapiterkey(it *hiter) (key unsafe.Pointer) { + return it.key +} + +func mapiterelem(it *hiter) (elem unsafe.Pointer) { + return it.elem +} + +//go:linkname mapiternext github.com/goplus/llgo/internal/runtime.mapiternext +func mapiternext(it *hiter) + +//go:linkname mapclear github.com/goplus/llgo/internal/runtime.mapclear +func mapclear(t *abi.Type, m unsafe.Pointer) + // MakeSlice creates a new zero-initialized slice value // for the specified slice type, length, and capacity. func MakeSlice(typ Type, len, cap int) Value { From ecba13c38eff6d557a0ea3b6499cc9ba6925c02b Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 31 Oct 2024 07:04:07 +0800 Subject: [PATCH 2/4] internal/lib/reflect: use sync.Map --- internal/lib/reflect/type.go | 77 +++++++++++++++++------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/internal/lib/reflect/type.go b/internal/lib/reflect/type.go index 4b6f4410..a1773775 100644 --- a/internal/lib/reflect/type.go +++ b/internal/lib/reflect/type.go @@ -1024,19 +1024,8 @@ func rtypeOf(i any) *abi.Type { return eface.typ } -/* TODO(xsw): // ptrMap is the cache for PointerTo. var ptrMap sync.Map // map[*rtype]*ptrType -*/ - -var ptrMap struct { - sync.Mutex - m map[*rtype]*ptrType -} - -func init() { - ptrMap.m = make(map[*rtype]*ptrType) -} // PtrTo returns the pointer type with element t. // For example, if t represents type Foo, PtrTo(t) represents *Foo. @@ -1057,8 +1046,8 @@ func (t *rtype) ptrTo() *abi.Type { return at.PtrToThis_ } // Check the cache. - if pi, ok := ptrMap.m[t]; ok { - return &pi.Type + if pi, ok := ptrMap.Load(t); ok { + return &pi.(*ptrType).Type } // Look in known types. @@ -1093,10 +1082,8 @@ func (t *rtype) ptrTo() *abi.Type { pp.Elem = at - ptrMap.m[t] = &pp - return &pp.Type - //pi, _ := ptrMap.LoadOrStore(t, &pp) - //return &pi.(*ptrType).Type + pi, _ := ptrMap.LoadOrStore(t, &pp) + return &pi.(*ptrType).Type } func ptrTo(t *abi.Type) *abi.Type { @@ -1300,31 +1287,27 @@ func haveIdenticalUnderlyingType(T, V *abi.Type, cmpTags bool) bool { func SliceOf(t Type) Type { typ := t.common() - /* TODO(xsw): no cache // Look in cache. ckey := cacheKey{Slice, typ, nil, 0} if slice, ok := lookupCache.Load(ckey); ok { return slice.(Type) } - // Look in known types. - s := "[]" + stringFor(typ) - for _, tt := range typesByString(s) { - slice := (*sliceType)(unsafe.Pointer(tt)) - if slice.Elem == typ { - ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) - return ti.(Type) + /* + // Look in known types. + s := "[]" + stringFor(typ) + for _, tt := range typesByString(s) { + slice := (*sliceType)(unsafe.Pointer(tt)) + if slice.Elem == typ { + ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + return ti.(Type) + } } - } */ slice := runtime.SliceOf(typ) - - // TODO(xsw): - // ti, _ := lookupCache.LoadOrStore(ckey, toRType(&slice.Type)) - // return ti.(Type) - - return toType(slice) + ti, _ := lookupCache.LoadOrStore(ckey, toRType(slice)) + return ti.(Type) } // toType converts from a *rtype to a Type that can be returned @@ -1339,6 +1322,19 @@ func toType(t *abi.Type) Type { return toRType(t) } +// The lookupCache caches ArrayOf, ChanOf, MapOf and SliceOf lookups. +var lookupCache sync.Map // map[cacheKey]*rtype + +// A cacheKey is the key for use in the lookupCache. +// Four values describe any of the types we are looking for: +// type kind, one or two subtypes, and an extra integer. +type cacheKey struct { + kind Kind + t1 *abi.Type + t2 *abi.Type + extra uintptr +} + // The funcLookupCache caches FuncOf lookups. // FuncOf does not share the common lookupCache since cacheKey is not // sufficient to represent functions unambiguously. @@ -1347,11 +1343,7 @@ var funcLookupCache struct { // m is a map[uint32][]*rtype keyed by the hash calculated in FuncOf. // Elements of m are append-only and thus safe for concurrent reading. - m map[uint32][]*abi.Type -} - -func init() { - funcLookupCache.m = make(map[uint32][]*abi.Type) + m sync.Map } // FuncOf returns the function type with the given argument and result types. @@ -1402,8 +1394,8 @@ func FuncOf(in, out []Type, variadic bool) Type { defer funcLookupCache.Unlock() // Look in cache. - if ts, ok := funcLookupCache.m[hash]; ok { - for _, t := range ts { + if ts, ok := funcLookupCache.m.Load(hash); ok { + for _, t := range ts.([]*abi.Type) { if haveIdenticalUnderlyingType(&ft.Type, t, true) { return toRType(t) } @@ -1411,8 +1403,11 @@ func FuncOf(in, out []Type, variadic bool) Type { } addToCache := func(tt *abi.Type) Type { - rts := funcLookupCache.m[hash] - funcLookupCache.m[hash] = append(rts, tt) + var rts []*abi.Type + if rti, ok := funcLookupCache.m.Load(hash); ok { + rts = rti.([]*abi.Type) + } + funcLookupCache.m.Store(hash, append(rts, tt)) return toType(tt) } str := funcStr(ft) From 2412760f1ca3d87874ba7503b6e0315f50dd0f1f Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 27 Nov 2024 10:32:19 +0800 Subject: [PATCH 3/4] internal/lib/reflect: makemap --- internal/lib/reflect/type.go | 73 +++++++++++++++++++++++++++++++++++ internal/lib/reflect/value.go | 34 +++++++++++++--- 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/internal/lib/reflect/type.go b/internal/lib/reflect/type.go index a1773775..6f3da0b6 100644 --- a/internal/lib/reflect/type.go +++ b/internal/lib/reflect/type.go @@ -1463,6 +1463,79 @@ func funcStr(ft *funcType) string { return string(repr) } +// MapOf returns the map type with the given key and element types. +// For example, if k represents int and e represents string, +// MapOf(k, e) represents map[int]string. +// +// If the key type is not a valid map key type (that is, if it does +// not implement Go's == operator), MapOf panics. +func MapOf(key, elem Type) Type { + ktyp := key.common() + etyp := elem.common() + + if ktyp.Equal == nil { + panic("reflect.MapOf: invalid key type " + stringFor(ktyp)) + } + + // Look in cache. + ckey := cacheKey{Map, ktyp, etyp, 0} + if mt, ok := lookupCache.Load(ckey); ok { + return mt.(Type) + } + + // Look in known types. + s := "map[" + stringFor(ktyp) + "]" + stringFor(etyp) + // for _, tt := range typesByString(s) { + // mt := (*mapType)(unsafe.Pointer(tt)) + // if mt.Key == ktyp && mt.Elem == etyp { + // ti, _ := lookupCache.LoadOrStore(ckey, toRType(tt)) + // return ti.(Type) + // } + // } + + // Make a map type. + // Note: flag values must match those used in the TMAP case + // in ../cmd/compile/internal/reflectdata/reflect.go:writeType. + var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil) + mt := **(**mapType)(unsafe.Pointer(&imap)) + mt.Str_ = s + mt.TFlag = 0 + mt.Hash = fnv1(etyp.Hash, 'm', byte(ktyp.Hash>>24), byte(ktyp.Hash>>16), byte(ktyp.Hash>>8), byte(ktyp.Hash)) + mt.Key = ktyp + mt.Elem = etyp + mt.Bucket = bucketOf(ktyp, etyp) + mt.Hasher = func(p unsafe.Pointer, seed uintptr) uintptr { + return typehash(ktyp, p, seed) + } + mt.Flags = 0 + if ktyp.Size_ > maxKeySize { + mt.KeySize = uint8(goarch.PtrSize) + mt.Flags |= 1 // indirect key + } else { + mt.KeySize = uint8(ktyp.Size_) + } + if etyp.Size_ > maxValSize { + mt.ValueSize = uint8(goarch.PtrSize) + mt.Flags |= 2 // indirect value + } else { + mt.MapType.ValueSize = uint8(etyp.Size_) + } + mt.MapType.BucketSize = uint16(mt.Bucket.Size_) + if isReflexive(ktyp) { + mt.Flags |= 4 + } + if needKeyUpdate(ktyp) { + mt.Flags |= 8 + } + if hashMightPanic(ktyp) { + mt.Flags |= 16 + } + mt.PtrToThis_ = nil + + ti, _ := lookupCache.LoadOrStore(ckey, toRType(&mt.Type)) + return ti.(Type) +} + // isReflexive reports whether the == operation on the type is reflexive. // That is, x == x for all values x of type t. func isReflexive(t *abi.Type) bool { diff --git a/internal/lib/reflect/value.go b/internal/lib/reflect/value.go index 60800460..0fd637fd 100644 --- a/internal/lib/reflect/value.go +++ b/internal/lib/reflect/value.go @@ -2297,9 +2297,9 @@ func (v Value) MapIndex(key Value) Value { } else { k = unsafe.Pointer(&key.ptr) } - e = mapaccess(v.typ(), v.pointer(), k) - // } - if e == nil { + var ok bool + e, ok = mapaccess(v.typ(), v.pointer(), k) + if !ok { return Value{} } typ := tt.Elem @@ -2519,7 +2519,7 @@ func (v Value) MapRange() *MapIter { // Force slow panicking path not inlined, so it won't add to the // inlining budget of the caller. -// TODO: undo when the inliner is no longer bottom-up only. +// TODO undo when the inliner is no longer bottom-up only. // //go:noinline func (f flag) panicNotMap() { @@ -2587,11 +2587,14 @@ func chancap(ch unsafe.Pointer) int //go:linkname chanlen github.com/goplus/llgo/internal/runtime.ChanLen func chanlen(ch unsafe.Pointer) int +//go:linkname makemap github.com/goplus/llgo/internal/runtime.MakeMap +func makemap(t *abi.Type, cap int) (m unsafe.Pointer) + //go:linkname maplen github.com/goplus/llgo/internal/runtime.MapLen func maplen(ch unsafe.Pointer) int -//go:linkname mapaccess github.com/goplus/llgo/internal/runtime.MapAccess1 -func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer) +//go:linkname mapaccess github.com/goplus/llgo/internal/runtime.MapAccess2 +func mapaccess(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) (val unsafe.Pointer, ok bool) //go:linkname mapassign0 github.com/goplus/llgo/internal/runtime.MapAssign func mapassign0(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) unsafe.Pointer @@ -2635,6 +2638,9 @@ func mapiternext(it *hiter) //go:linkname mapclear github.com/goplus/llgo/internal/runtime.mapclear func mapclear(t *abi.Type, m unsafe.Pointer) +//go:linkname typehash github.com/goplus/llgo/internal/runtime.typehash +func typehash(t *abi.Type, p unsafe.Pointer, h uintptr) uintptr + // MakeSlice creates a new zero-initialized slice value // for the specified slice type, length, and capacity. func MakeSlice(typ Type, len, cap int) Value { @@ -2655,6 +2661,22 @@ func MakeSlice(typ Type, len, cap int) Value { return Value{&typ.(*rtype).t, unsafe.Pointer(&s), flagIndir | flag(Slice)} } +// MakeMap creates a new map with the specified type. +func MakeMap(typ Type) Value { + return MakeMapWithSize(typ, 0) +} + +// MakeMapWithSize creates a new map with the specified type +// and initial space for approximately n elements. +func MakeMapWithSize(typ Type, n int) Value { + if typ.Kind() != Map { + panic("reflect.MakeMapWithSize of non-map type") + } + t := typ.common() + m := makemap(t, n) + return Value{t, m, flag(Map)} +} + func ifaceE2I(t *abi.Type, src any, dst unsafe.Pointer) { panic("todo: reflect.ifaceE2I") } From 91b46b05ad1edde514d7ea5efd08cb8265ed905a Mon Sep 17 00:00:00 2001 From: visualfc Date: Wed, 27 Nov 2024 11:31:28 +0800 Subject: [PATCH 4/4] cl/_testgo/reflect: map demo --- cl/_testgo/reflect/in.go | 62 ++++++ cl/_testgo/reflect/out.ll | 440 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 502 insertions(+) diff --git a/cl/_testgo/reflect/in.go b/cl/_testgo/reflect/in.go index 8d520649..482814f7 100644 --- a/cl/_testgo/reflect/in.go +++ b/cl/_testgo/reflect/in.go @@ -11,6 +11,8 @@ func main() { callClosure() callMethod() callIMethod() + mapDemo1() + mapDemo2() } func demo(n1, n2, n3, n4, n5, n6, n7, n8, n9 int, a ...interface{}) (int, int) { @@ -119,3 +121,63 @@ func callIMethod() { r2 := v2.Call([]reflect.Value{reflect.ValueOf(100)}) println(r2[0].Int()) } + +func mapDemo1() { + m := map[int]string{ + 1: "hello", + 2: "world", + } + v := reflect.ValueOf(m) + if v.Len() != 2 || len(v.MapKeys()) != 2 { + panic("error") + } + if v.MapIndex(reflect.ValueOf(2)).String() != "world" { + panic("MapIndex error") + } + v.SetMapIndex(reflect.ValueOf(2), reflect.ValueOf("todo")) + if v.MapIndex(reflect.ValueOf(2)).String() != "todo" { + panic("MapIndex error") + } + if v.MapIndex(reflect.ValueOf(0)).IsValid() { + println("must invalid") + } + key := reflect.New(v.Type().Key()).Elem() + value := reflect.New(v.Type().Elem()).Elem() + iter := v.MapRange() + for iter.Next() { + key.SetIterKey(iter) + value.SetIterValue(iter) + if key.Int() != iter.Key().Int() || value.String() != iter.Value().String() { + panic("MapIter error") + } + } +} + +func mapDemo2() { + v := reflect.MakeMap(reflect.MapOf(reflect.TypeOf(0), reflect.TypeOf(""))) + v.SetMapIndex(reflect.ValueOf(1), reflect.ValueOf("hello")) + v.SetMapIndex(reflect.ValueOf(2), reflect.ValueOf("world")) + if v.Len() != 2 || len(v.MapKeys()) != 2 { + panic("error") + } + if v.MapIndex(reflect.ValueOf(2)).String() != "world" { + panic("MapIndex error") + } + v.SetMapIndex(reflect.ValueOf(2), reflect.ValueOf("todo")) + if v.MapIndex(reflect.ValueOf(2)).String() != "todo" { + panic("MapIndex error") + } + if v.MapIndex(reflect.ValueOf(0)).IsValid() { + println("must invalid") + } + key := reflect.New(v.Type().Key()).Elem() + value := reflect.New(v.Type().Elem()).Elem() + iter := v.MapRange() + for iter.Next() { + key.SetIterKey(iter) + value.SetIterValue(iter) + if key.Int() != iter.Key().Int() || value.String() != iter.Value().String() { + panic("MapIter error") + } + } +} diff --git a/cl/_testgo/reflect/out.ll b/cl/_testgo/reflect/out.ll index 4982e4f3..dc4f4a83 100644 --- a/cl/_testgo/reflect/out.ll +++ b/cl/_testgo/reflect/out.ll @@ -44,6 +44,17 @@ source_filename = "main" @16 = private unnamed_addr constant [21 x i8] c"type assertion failed", align 1 @__llgo_argc = global i32 0, align 4 @__llgo_argv = global ptr null, align 8 +@"map[_llgo_int]_llgo_string" = linkonce global ptr null, align 8 +@17 = private unnamed_addr constant [7 x i8] c"topbits", align 1 +@18 = private unnamed_addr constant [4 x i8] c"keys", align 1 +@19 = private unnamed_addr constant [5 x i8] c"elems", align 1 +@20 = private unnamed_addr constant [8 x i8] c"overflow", align 1 +@21 = private unnamed_addr constant [5 x i8] c"hello", align 1 +@22 = private unnamed_addr constant [5 x i8] c"world", align 1 +@23 = private unnamed_addr constant [14 x i8] c"MapIndex error", align 1 +@24 = private unnamed_addr constant [4 x i8] c"todo", align 1 +@25 = private unnamed_addr constant [12 x i8] c"must invalid", align 1 +@26 = private unnamed_addr constant [13 x i8] c"MapIter error", align 1 define i64 @"main.(*T).Add"(ptr %0, i64 %1) { _llgo_0: @@ -639,9 +650,357 @@ _llgo_0: call void @main.callClosure() call void @main.callMethod() call void @main.callIMethod() + call void @main.mapDemo1() + call void @main.mapDemo2() ret i32 0 } +define void @main.mapDemo1() { +_llgo_0: + %0 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %1 = call ptr @"github.com/goplus/llgo/internal/runtime.MakeMap"(ptr %0, i64 2) + %2 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %3 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + store i64 1, ptr %3, align 4 + %4 = call ptr @"github.com/goplus/llgo/internal/runtime.MapAssign"(ptr %2, ptr %1, ptr %3) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @21, i64 5 }, ptr %4, align 8 + %5 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %6 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 8) + store i64 2, ptr %6, align 4 + %7 = call ptr @"github.com/goplus/llgo/internal/runtime.MapAssign"(ptr %5, ptr %1, ptr %6) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @22, i64 5 }, ptr %7, align 8 + %8 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %9 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %8, 0 + %10 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %9, ptr %1, 1 + %11 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %10) + %12 = call i64 @reflect.Value.Len(%reflect.Value %11) + %13 = icmp ne i64 %12, 2 + br i1 %13, label %_llgo_1, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_3, %_llgo_0 + %14 = load ptr, ptr @_llgo_string, align 8 + %15 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @5, i64 5 }, ptr %15, align 8 + %16 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %14, 0 + %17 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %16, ptr %15, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %17) + unreachable + +_llgo_2: ; preds = %_llgo_3 + %18 = load ptr, ptr @_llgo_int, align 8 + %19 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %18, 0 + %20 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %19, ptr inttoptr (i64 2 to ptr), 1 + %21 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %20) + %22 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %11, %reflect.Value %21) + %23 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %22) + %24 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %23, %"github.com/goplus/llgo/internal/runtime.String" { ptr @22, i64 5 }) + %25 = xor i1 %24, true + br i1 %25, label %_llgo_4, label %_llgo_5 + +_llgo_3: ; preds = %_llgo_0 + %26 = call %"github.com/goplus/llgo/internal/runtime.Slice" @reflect.Value.MapKeys(%reflect.Value %11) + %27 = extractvalue %"github.com/goplus/llgo/internal/runtime.Slice" %26, 1 + %28 = icmp ne i64 %27, 2 + br i1 %28, label %_llgo_1, label %_llgo_2 + +_llgo_4: ; preds = %_llgo_2 + %29 = load ptr, ptr @_llgo_string, align 8 + %30 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @23, i64 14 }, ptr %30, align 8 + %31 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %29, 0 + %32 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %31, ptr %30, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %32) + unreachable + +_llgo_5: ; preds = %_llgo_2 + %33 = load ptr, ptr @_llgo_int, align 8 + %34 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %33, 0 + %35 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %34, ptr inttoptr (i64 2 to ptr), 1 + %36 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %35) + %37 = load ptr, ptr @_llgo_string, align 8 + %38 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @24, i64 4 }, ptr %38, align 8 + %39 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %37, 0 + %40 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %39, ptr %38, 1 + %41 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %40) + call void @reflect.Value.SetMapIndex(%reflect.Value %11, %reflect.Value %36, %reflect.Value %41) + %42 = load ptr, ptr @_llgo_int, align 8 + %43 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %42, 0 + %44 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %43, ptr inttoptr (i64 2 to ptr), 1 + %45 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %44) + %46 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %11, %reflect.Value %45) + %47 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %46) + %48 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %47, %"github.com/goplus/llgo/internal/runtime.String" { ptr @24, i64 4 }) + %49 = xor i1 %48, true + br i1 %49, label %_llgo_6, label %_llgo_7 + +_llgo_6: ; preds = %_llgo_5 + %50 = load ptr, ptr @_llgo_string, align 8 + %51 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @23, i64 14 }, ptr %51, align 8 + %52 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %50, 0 + %53 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %52, ptr %51, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %53) + unreachable + +_llgo_7: ; preds = %_llgo_5 + %54 = load ptr, ptr @_llgo_int, align 8 + %55 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %54, 0 + %56 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %55, ptr null, 1 + %57 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %56) + %58 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %11, %reflect.Value %57) + %59 = call i1 @reflect.Value.IsValid(%reflect.Value %58) + br i1 %59, label %_llgo_8, label %_llgo_9 + +_llgo_8: ; preds = %_llgo_7 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @25, i64 12 }) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_9 + +_llgo_9: ; preds = %_llgo_8, %_llgo_7 + %60 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %11) + %61 = call ptr @"github.com/goplus/llgo/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/internal/runtime.iface" %60) + %62 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %60, 0 + %63 = getelementptr ptr, ptr %62, i64 18 + %64 = load ptr, ptr %63, align 8 + %65 = insertvalue { ptr, ptr } undef, ptr %64, 0 + %66 = insertvalue { ptr, ptr } %65, ptr %61, 1 + %67 = extractvalue { ptr, ptr } %66, 1 + %68 = extractvalue { ptr, ptr } %66, 0 + %69 = call %"github.com/goplus/llgo/internal/runtime.iface" %68(ptr %67) + %70 = call %reflect.Value @reflect.New(%"github.com/goplus/llgo/internal/runtime.iface" %69) + %71 = call %reflect.Value @reflect.Value.Elem(%reflect.Value %70) + %72 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %11) + %73 = call ptr @"github.com/goplus/llgo/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/internal/runtime.iface" %72) + %74 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %72, 0 + %75 = getelementptr ptr, ptr %74, i64 9 + %76 = load ptr, ptr %75, align 8 + %77 = insertvalue { ptr, ptr } undef, ptr %76, 0 + %78 = insertvalue { ptr, ptr } %77, ptr %73, 1 + %79 = extractvalue { ptr, ptr } %78, 1 + %80 = extractvalue { ptr, ptr } %78, 0 + %81 = call %"github.com/goplus/llgo/internal/runtime.iface" %80(ptr %79) + %82 = call %reflect.Value @reflect.New(%"github.com/goplus/llgo/internal/runtime.iface" %81) + %83 = call %reflect.Value @reflect.Value.Elem(%reflect.Value %82) + %84 = call ptr @reflect.Value.MapRange(%reflect.Value %11) + br label %_llgo_12 + +_llgo_10: ; preds = %_llgo_12 + call void @reflect.Value.SetIterKey(%reflect.Value %71, ptr %84) + call void @reflect.Value.SetIterValue(%reflect.Value %83, ptr %84) + %85 = call i64 @reflect.Value.Int(%reflect.Value %71) + %86 = call %reflect.Value @"reflect.(*MapIter).Key"(ptr %84) + %87 = call i64 @reflect.Value.Int(%reflect.Value %86) + %88 = icmp ne i64 %85, %87 + br i1 %88, label %_llgo_13, label %_llgo_14 + +_llgo_11: ; preds = %_llgo_12 + ret void + +_llgo_12: ; preds = %_llgo_14, %_llgo_9 + %89 = call i1 @"reflect.(*MapIter).Next"(ptr %84) + br i1 %89, label %_llgo_10, label %_llgo_11 + +_llgo_13: ; preds = %_llgo_14, %_llgo_10 + %90 = load ptr, ptr @_llgo_string, align 8 + %91 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @26, i64 13 }, ptr %91, align 8 + %92 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %90, 0 + %93 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %92, ptr %91, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %93) + unreachable + +_llgo_14: ; preds = %_llgo_10 + %94 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %83) + %95 = call %reflect.Value @"reflect.(*MapIter).Value"(ptr %84) + %96 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %95) + %97 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %94, %"github.com/goplus/llgo/internal/runtime.String" %96) + %98 = xor i1 %97, true + br i1 %98, label %_llgo_13, label %_llgo_12 +} + +define void @main.mapDemo2() { +_llgo_0: + %0 = load ptr, ptr @_llgo_int, align 8 + %1 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %0, 0 + %2 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %1, ptr null, 1 + %3 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.TypeOf(%"github.com/goplus/llgo/internal/runtime.eface" %2) + %4 = load ptr, ptr @_llgo_string, align 8 + %5 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, ptr %5, align 8 + %6 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %4, 0 + %7 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %6, ptr %5, 1 + %8 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.TypeOf(%"github.com/goplus/llgo/internal/runtime.eface" %7) + %9 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.MapOf(%"github.com/goplus/llgo/internal/runtime.iface" %3, %"github.com/goplus/llgo/internal/runtime.iface" %8) + %10 = call %reflect.Value @reflect.MakeMap(%"github.com/goplus/llgo/internal/runtime.iface" %9) + %11 = load ptr, ptr @_llgo_int, align 8 + %12 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %11, 0 + %13 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %12, ptr inttoptr (i64 1 to ptr), 1 + %14 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %13) + %15 = load ptr, ptr @_llgo_string, align 8 + %16 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @21, i64 5 }, ptr %16, align 8 + %17 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %15, 0 + %18 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %17, ptr %16, 1 + %19 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %18) + call void @reflect.Value.SetMapIndex(%reflect.Value %10, %reflect.Value %14, %reflect.Value %19) + %20 = load ptr, ptr @_llgo_int, align 8 + %21 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %20, 0 + %22 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %21, ptr inttoptr (i64 2 to ptr), 1 + %23 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %22) + %24 = load ptr, ptr @_llgo_string, align 8 + %25 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @22, i64 5 }, ptr %25, align 8 + %26 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %24, 0 + %27 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %26, ptr %25, 1 + %28 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %27) + call void @reflect.Value.SetMapIndex(%reflect.Value %10, %reflect.Value %23, %reflect.Value %28) + %29 = call i64 @reflect.Value.Len(%reflect.Value %10) + %30 = icmp ne i64 %29, 2 + br i1 %30, label %_llgo_1, label %_llgo_3 + +_llgo_1: ; preds = %_llgo_3, %_llgo_0 + %31 = load ptr, ptr @_llgo_string, align 8 + %32 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @5, i64 5 }, ptr %32, align 8 + %33 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %31, 0 + %34 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %33, ptr %32, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %34) + unreachable + +_llgo_2: ; preds = %_llgo_3 + %35 = load ptr, ptr @_llgo_int, align 8 + %36 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %35, 0 + %37 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %36, ptr inttoptr (i64 2 to ptr), 1 + %38 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %37) + %39 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %10, %reflect.Value %38) + %40 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %39) + %41 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %40, %"github.com/goplus/llgo/internal/runtime.String" { ptr @22, i64 5 }) + %42 = xor i1 %41, true + br i1 %42, label %_llgo_4, label %_llgo_5 + +_llgo_3: ; preds = %_llgo_0 + %43 = call %"github.com/goplus/llgo/internal/runtime.Slice" @reflect.Value.MapKeys(%reflect.Value %10) + %44 = extractvalue %"github.com/goplus/llgo/internal/runtime.Slice" %43, 1 + %45 = icmp ne i64 %44, 2 + br i1 %45, label %_llgo_1, label %_llgo_2 + +_llgo_4: ; preds = %_llgo_2 + %46 = load ptr, ptr @_llgo_string, align 8 + %47 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @23, i64 14 }, ptr %47, align 8 + %48 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %46, 0 + %49 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %48, ptr %47, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %49) + unreachable + +_llgo_5: ; preds = %_llgo_2 + %50 = load ptr, ptr @_llgo_int, align 8 + %51 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %50, 0 + %52 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %51, ptr inttoptr (i64 2 to ptr), 1 + %53 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %52) + %54 = load ptr, ptr @_llgo_string, align 8 + %55 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @24, i64 4 }, ptr %55, align 8 + %56 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %54, 0 + %57 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %56, ptr %55, 1 + %58 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %57) + call void @reflect.Value.SetMapIndex(%reflect.Value %10, %reflect.Value %53, %reflect.Value %58) + %59 = load ptr, ptr @_llgo_int, align 8 + %60 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %59, 0 + %61 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %60, ptr inttoptr (i64 2 to ptr), 1 + %62 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %61) + %63 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %10, %reflect.Value %62) + %64 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %63) + %65 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %64, %"github.com/goplus/llgo/internal/runtime.String" { ptr @24, i64 4 }) + %66 = xor i1 %65, true + br i1 %66, label %_llgo_6, label %_llgo_7 + +_llgo_6: ; preds = %_llgo_5 + %67 = load ptr, ptr @_llgo_string, align 8 + %68 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @23, i64 14 }, ptr %68, align 8 + %69 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %67, 0 + %70 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %69, ptr %68, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %70) + unreachable + +_llgo_7: ; preds = %_llgo_5 + %71 = load ptr, ptr @_llgo_int, align 8 + %72 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %71, 0 + %73 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %72, ptr null, 1 + %74 = call %reflect.Value @reflect.ValueOf(%"github.com/goplus/llgo/internal/runtime.eface" %73) + %75 = call %reflect.Value @reflect.Value.MapIndex(%reflect.Value %10, %reflect.Value %74) + %76 = call i1 @reflect.Value.IsValid(%reflect.Value %75) + br i1 %76, label %_llgo_8, label %_llgo_9 + +_llgo_8: ; preds = %_llgo_7 + call void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @25, i64 12 }) + call void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8 10) + br label %_llgo_9 + +_llgo_9: ; preds = %_llgo_8, %_llgo_7 + %77 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %10) + %78 = call ptr @"github.com/goplus/llgo/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/internal/runtime.iface" %77) + %79 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %77, 0 + %80 = getelementptr ptr, ptr %79, i64 18 + %81 = load ptr, ptr %80, align 8 + %82 = insertvalue { ptr, ptr } undef, ptr %81, 0 + %83 = insertvalue { ptr, ptr } %82, ptr %78, 1 + %84 = extractvalue { ptr, ptr } %83, 1 + %85 = extractvalue { ptr, ptr } %83, 0 + %86 = call %"github.com/goplus/llgo/internal/runtime.iface" %85(ptr %84) + %87 = call %reflect.Value @reflect.New(%"github.com/goplus/llgo/internal/runtime.iface" %86) + %88 = call %reflect.Value @reflect.Value.Elem(%reflect.Value %87) + %89 = call %"github.com/goplus/llgo/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %10) + %90 = call ptr @"github.com/goplus/llgo/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/internal/runtime.iface" %89) + %91 = extractvalue %"github.com/goplus/llgo/internal/runtime.iface" %89, 0 + %92 = getelementptr ptr, ptr %91, i64 9 + %93 = load ptr, ptr %92, align 8 + %94 = insertvalue { ptr, ptr } undef, ptr %93, 0 + %95 = insertvalue { ptr, ptr } %94, ptr %90, 1 + %96 = extractvalue { ptr, ptr } %95, 1 + %97 = extractvalue { ptr, ptr } %95, 0 + %98 = call %"github.com/goplus/llgo/internal/runtime.iface" %97(ptr %96) + %99 = call %reflect.Value @reflect.New(%"github.com/goplus/llgo/internal/runtime.iface" %98) + %100 = call %reflect.Value @reflect.Value.Elem(%reflect.Value %99) + %101 = call ptr @reflect.Value.MapRange(%reflect.Value %10) + br label %_llgo_12 + +_llgo_10: ; preds = %_llgo_12 + call void @reflect.Value.SetIterKey(%reflect.Value %88, ptr %101) + call void @reflect.Value.SetIterValue(%reflect.Value %100, ptr %101) + %102 = call i64 @reflect.Value.Int(%reflect.Value %88) + %103 = call %reflect.Value @"reflect.(*MapIter).Key"(ptr %101) + %104 = call i64 @reflect.Value.Int(%reflect.Value %103) + %105 = icmp ne i64 %102, %104 + br i1 %105, label %_llgo_13, label %_llgo_14 + +_llgo_11: ; preds = %_llgo_12 + ret void + +_llgo_12: ; preds = %_llgo_14, %_llgo_9 + %106 = call i1 @"reflect.(*MapIter).Next"(ptr %101) + br i1 %106, label %_llgo_10, label %_llgo_11 + +_llgo_13: ; preds = %_llgo_14, %_llgo_10 + %107 = load ptr, ptr @_llgo_string, align 8 + %108 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 16) + store %"github.com/goplus/llgo/internal/runtime.String" { ptr @26, i64 13 }, ptr %108, align 8 + %109 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" undef, ptr %107, 0 + %110 = insertvalue %"github.com/goplus/llgo/internal/runtime.eface" %109, ptr %108, 1 + call void @"github.com/goplus/llgo/internal/runtime.Panic"(%"github.com/goplus/llgo/internal/runtime.eface" %110) + unreachable + +_llgo_14: ; preds = %_llgo_10 + %111 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %100) + %112 = call %reflect.Value @"reflect.(*MapIter).Value"(ptr %101) + %113 = call %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value %112) + %114 = call i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String" %111, %"github.com/goplus/llgo/internal/runtime.String" %113) + %115 = xor i1 %114, true + br i1 %115, label %_llgo_13, label %_llgo_12 +} + declare void @"github.com/goplus/llgo/internal/runtime.PrintString"(%"github.com/goplus/llgo/internal/runtime.String") declare void @"github.com/goplus/llgo/internal/runtime.PrintByte"(i8) @@ -948,6 +1307,43 @@ _llgo_22: ; preds = %_llgo_21, %_llgo_20 %173 = insertvalue %"github.com/goplus/llgo/internal/runtime.Slice" %172, i64 2, 2 %174 = call ptr @"github.com/goplus/llgo/internal/runtime.Struct"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @3, i64 4 }, i64 16, %"github.com/goplus/llgo/internal/runtime.Slice" %173) store ptr %174, ptr @"main.struct$zCLFE3aa581X7nuJztqlq4JjJDbHkfoMY0CexWOzH8A", align 8 + %175 = load ptr, ptr @"map[_llgo_int]_llgo_string", align 8 + %176 = icmp eq ptr %175, null + br i1 %176, label %_llgo_23, label %_llgo_24 + +_llgo_23: ; preds = %_llgo_22 + %177 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 34) + %178 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24) + %179 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 40) + %180 = call ptr @"github.com/goplus/llgo/internal/runtime.ArrayOf"(i64 8, ptr %179) + %181 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @17, i64 7 }, ptr %180, i64 0, %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, i1 false) + %182 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 34) + %183 = call ptr @"github.com/goplus/llgo/internal/runtime.ArrayOf"(i64 8, ptr %182) + %184 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @18, i64 4 }, ptr %183, i64 8, %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, i1 false) + %185 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 24) + %186 = call ptr @"github.com/goplus/llgo/internal/runtime.ArrayOf"(i64 8, ptr %185) + %187 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @19, i64 5 }, ptr %186, i64 72, %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, i1 false) + %188 = call ptr @"github.com/goplus/llgo/internal/runtime.Basic"(i64 58) + %189 = call %"github.com/goplus/llgo/internal/abi.StructField" @"github.com/goplus/llgo/internal/runtime.StructField"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @20, i64 8 }, ptr %188, i64 200, %"github.com/goplus/llgo/internal/runtime.String" zeroinitializer, i1 false) + %190 = call ptr @"github.com/goplus/llgo/internal/runtime.AllocU"(i64 224) + %191 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %190, i64 0 + store %"github.com/goplus/llgo/internal/abi.StructField" %181, ptr %191, align 8 + %192 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %190, i64 1 + store %"github.com/goplus/llgo/internal/abi.StructField" %184, ptr %192, align 8 + %193 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %190, i64 2 + store %"github.com/goplus/llgo/internal/abi.StructField" %187, ptr %193, align 8 + %194 = getelementptr %"github.com/goplus/llgo/internal/abi.StructField", ptr %190, i64 3 + store %"github.com/goplus/llgo/internal/abi.StructField" %189, ptr %194, align 8 + %195 = insertvalue %"github.com/goplus/llgo/internal/runtime.Slice" undef, ptr %190, 0 + %196 = insertvalue %"github.com/goplus/llgo/internal/runtime.Slice" %195, i64 4, 1 + %197 = insertvalue %"github.com/goplus/llgo/internal/runtime.Slice" %196, i64 4, 2 + %198 = call ptr @"github.com/goplus/llgo/internal/runtime.Struct"(%"github.com/goplus/llgo/internal/runtime.String" { ptr @3, i64 4 }, i64 208, %"github.com/goplus/llgo/internal/runtime.Slice" %197) + %199 = call ptr @"github.com/goplus/llgo/internal/runtime.MapOf"(ptr %177, ptr %178, ptr %198, i64 4) + call void @"github.com/goplus/llgo/internal/runtime.SetDirectIface"(ptr %199) + store ptr %199, ptr @"map[_llgo_int]_llgo_string", align 8 + br label %_llgo_24 + +_llgo_24: ; preds = %_llgo_23, %_llgo_22 ret void } @@ -1016,3 +1412,47 @@ declare %"github.com/goplus/llgo/internal/runtime.Slice" @reflect.Value.CallSlic declare void @reflect.init() declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @"github.com/goplus/llgo/internal/runtime.MapOf"(ptr, ptr, ptr, i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.ArrayOf"(i64, ptr) + +declare ptr @"github.com/goplus/llgo/internal/runtime.MakeMap"(ptr, i64) + +declare ptr @"github.com/goplus/llgo/internal/runtime.MapAssign"(ptr, ptr, ptr) + +declare i64 @reflect.Value.Len(%reflect.Value) + +declare %"github.com/goplus/llgo/internal/runtime.Slice" @reflect.Value.MapKeys(%reflect.Value) + +declare %reflect.Value @reflect.Value.MapIndex(%reflect.Value, %reflect.Value) + +declare %"github.com/goplus/llgo/internal/runtime.String" @reflect.Value.String(%reflect.Value) + +declare i1 @"github.com/goplus/llgo/internal/runtime.StringEqual"(%"github.com/goplus/llgo/internal/runtime.String", %"github.com/goplus/llgo/internal/runtime.String") + +declare void @reflect.Value.SetMapIndex(%reflect.Value, %reflect.Value, %reflect.Value) + +declare i1 @reflect.Value.IsValid(%reflect.Value) + +declare %reflect.Value @reflect.New(%"github.com/goplus/llgo/internal/runtime.iface") + +declare %reflect.Value @reflect.Value.Elem(%reflect.Value) + +declare ptr @reflect.Value.MapRange(%reflect.Value) + +declare i1 @"reflect.(*MapIter).Next"(ptr) + +declare void @reflect.Value.SetIterKey(%reflect.Value, ptr) + +declare void @reflect.Value.SetIterValue(%reflect.Value, ptr) + +declare %reflect.Value @"reflect.(*MapIter).Key"(ptr) + +declare %reflect.Value @"reflect.(*MapIter).Value"(ptr) + +declare %"github.com/goplus/llgo/internal/runtime.iface" @reflect.TypeOf(%"github.com/goplus/llgo/internal/runtime.eface") + +declare %"github.com/goplus/llgo/internal/runtime.iface" @reflect.MapOf(%"github.com/goplus/llgo/internal/runtime.iface", %"github.com/goplus/llgo/internal/runtime.iface") + +declare %reflect.Value @reflect.MakeMap(%"github.com/goplus/llgo/internal/runtime.iface")