package emit import ( "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/ssa" "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/types" ) func (e *irEmitter) isScalarType(t string) bool { return t == "i1" || t == "i8" || t == "i16" || t == "i32" || t == "i64" || t == "float" || t == "double" } func (e *irEmitter) fitsInPtr(t string) bool { if !e.isScalarType(t) { return false } if e.ptrBits == 32 && (t == "i64" || t == "double") { return false } return true } func (e *irEmitter) emitMakeInterface(m *ssa.SSAMakeInterface) { reg := e.regName(m) val := e.operand(m.X) valType := e.llvmType(m.X.SSAType()) if _, isAlloc := m.X.(*ssa.SSAAlloc); !isAlloc { if at, ok := e.allocTypes[m.X]; ok && at != "ptr" && at != "void" { valType = at } } if valType == e.ifaceType() { tp := e.nextReg2("mi") e.w(" ") ; e.w(tp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n") dp := e.nextReg2("mi") e.w(" ") ; e.w(dp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") t1 := e.nextReg2("mi") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue {ptr, ptr} undef, ptr ") ; e.w(tp) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} ") ; e.w(t1) ; e.w(", ptr ") ; e.w(dp) ; e.w(", 1\n") return } var valPtr string if valType == "void" { valPtr = "null" } else if valType == "ptr" { valPtr = val } else if e.fitsInPtr(valType) { ipt := e.intptrType() ext := "" if valType == ipt { ext = val } else if valType == "i1" || valType == "i8" || valType == "i16" || valType == "i32" { ext = e.nextReg2("mi") e.w(" ") ; e.w(ext) ; e.w(" = zext ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") } else if valType == "i64" { ext = val } else if valType == "float" { ftmp := e.nextReg2("mi") e.w(" ") ; e.w(ftmp) ; e.w(" = bitcast float ") ; e.w(val) ; e.w(" to i32\n") if ipt == "i32" { ext = ftmp } else { ext = e.nextReg2("mi") e.w(" ") ; e.w(ext) ; e.w(" = zext i32 ") ; e.w(ftmp) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") } } else if valType == "double" { dval := val if isConstOperand(val) { dval = e.nextReg2("mi") e.w(" ") ; e.w(dval) ; e.w(" = fadd double 0.0, ") ; e.w(ensureTokenFloatLit(val)) ; e.w("\n") } ext = e.nextReg2("mi") e.w(" ") ; e.w(ext) ; e.w(" = bitcast double ") ; e.w(dval) ; e.w(" to i64\n") } else { ext = val } valPtr = e.nextReg2("mi") e.w(" ") ; e.w(valPtr) ; e.w(" = inttoptr ") ; e.w(ipt) ; e.w(" ") ; e.w(ext) ; e.w(" to ptr\n") } else { ipt := e.intptrType() sz := e.nextReg2("ha") e.w(" ") ; e.w(sz) ; e.w(" = ptrtoint ptr getelementptr (") ; e.w(valType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") valPtr = e.nextReg2("mi") e.w(" ") ; e.w(valPtr) ; e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) ; e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n") e.w(" store ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") e.scopeTrackAlloc(valPtr) } typeid := e.typeIDGlobal(m.X.SSAType()) t1 := e.nextReg2("mi") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue {ptr, ptr} undef, ptr ") ; e.w(typeid) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} ") ; e.w(t1) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", 1\n") } func (e *irEmitter) typeIDGlobal(t syntax.Type) string { name := e.reflectTypeName(t) if e.typeIDs == nil { e.typeIDs = map[string]int32{} } if _, ok := e.typeIDs[name]; !ok { e.typeIDNext++ e.typeIDs[name] = e.typeIDNext } return "@\"" | name | "\"" } func (e *irEmitter) reflectTypeName(t syntax.Type) string { if b, ok := t.(*types.Basic); ok { switch b.Kind() { case types.Bool, types.UntypedBool: return "reflect/types.type:basic:bool" case types.Int8: return "reflect/types.type:basic:int8" case types.Int16: return "reflect/types.type:basic:int16" case types.Int32, types.UntypedInt, types.UntypedRune: return "reflect/types.type:basic:int32" case types.Int64: return "reflect/types.type:basic:int64" case types.Uint8: return "reflect/types.type:basic:uint8" case types.Uint16: return "reflect/types.type:basic:uint16" case types.Uint32: return "reflect/types.type:basic:uint32" case types.Uint64: return "reflect/types.type:basic:uint64" case types.Float32: return "reflect/types.type:basic:float32" case types.Float64, types.UntypedFloat: return "reflect/types.type:basic:float64" case types.TCString, types.UntypedString: return "reflect/types.type:basic:bytes" case types.UnsafePointer: return "reflect/types.type:basic:uintptr" } } if named, ok := t.(*types.Named); ok && named.Obj() != nil { pkg := "" if named.Obj().Pkg() != nil { pkg = named.Obj().Pkg().Path() } if pkg == "" { pkg = e.pkg.Pkg.Path() } result := "reflect/types.type:named:" | pkg | "." | named.Obj().Name() if pkg == e.pkg.Pkg.Path() { if e.localTypeIDs == nil { e.localTypeIDs = map[string]bool{} } e.localTypeIDs["\"" | result | "\""] = true } return result } if p, ok := t.(*types.Pointer); ok { inner := e.reflectTypeName(p.Elem()) if hasPrefix(inner, "reflect/types.type:") { result := "reflect/types.type:pointer:" | inner[len("reflect/types.type:"):] quoted := "\"" | result | "\"" if e.localTypeIDs != nil && e.localTypeIDs["\"" | inner | "\""] { e.localTypeIDs[quoted] = true } return result } return inner | ".ptr" } if _, ok := t.(*types.TCInterface); ok { result := "reflect/types.type:interface:{}" if e.localTypeIDs == nil { e.localTypeIDs = map[string]bool{} } e.localTypeIDs["\"" | result | "\""] = true return result } if sl, ok := t.(*types.Slice); ok { elem := sl.Elem() if b, ok := types.SafeUnderlying(elem).(*types.Basic); ok && b.Kind() == Uint8 { return "reflect/types.type:basic:bytes" } inner := e.reflectTypeName(elem) if hasPrefix(inner, "reflect/types.type:") { result := "reflect/types.type:slice:" | inner[len("reflect/types.type:"):] if e.localTypeIDs == nil { e.localTypeIDs = map[string]bool{} } e.localTypeIDs["\"" | result | "\""] = true return result } return inner | ".slice" } pkg := e.pkg.Pkg.Path() return pkg | ".typeid.unknown" } func (e *irEmitter) findIfaceImpls(methodName string) []ifaceImpl { var impls []ifaceImpl hasType := map[string]bool{} var memberKeys []string for mname := range e.pkg.Members { memberKeys = append(memberKeys, mname) } for i := 1; i < len(memberKeys); i++ { for j := i; j > 0 && memberKeys[j] < memberKeys[j-1]; j-- { memberKeys[j], memberKeys[j-1] = memberKeys[j-1], memberKeys[j] } } for _, mname := range memberKeys { m := e.pkg.Members[mname] fn, ok := m.(*ssa.SSAFunction) if !ok { continue } dotIdx := -1 for i := 0; i < len(mname); i++ { if mname[i] == '.' { dotIdx = i break } } if dotIdx < 0 { continue } if mname[dotIdx+1:] != methodName { continue } tname := mname[:dotIdx] looked := e.pkg.Pkg.Scope().Lookup(tname) if looked == nil { continue } tn, ok2 := looked.(*types.TypeName) if !ok2 || tn.Type() == nil { continue } isPtrRecv := fn.object != nil && fn.object.HasPtrRecv() recvT := tn.Type() if isPtrRecv { recvT = types.NewPointer(recvT) } impls = append(impls, ifaceImpl{fn: fn, recvType: recvT, ptrRecv: isPtrRecv}) hasType[tname] = true } scopeNames := e.pkg.Pkg.Scope().Names() for sni := 0; sni < len(scopeNames); sni++ { sname := scopeNames[sni] tn2, ok4 := e.pkg.Pkg.Scope().Lookup(sname).(*types.TypeName) if !ok4 || tn2.Type() == nil { continue } if hasType[sname] { continue } chain, fn, embedT := e.findEmbedMethod(tn2.Type(), methodName, 0) if fn != nil { isPtrRecv := fn.object != nil && fn.object.HasPtrRecv() impls = append(impls, ifaceImpl{ fn: fn, recvType: types.NewPointer(tn2.Type()), ptrRecv: isPtrRecv, embedField: chain[0], embedType: embedT, embedChain: chain, }) hasType[sname] = true } } var regKeys []string for pkgPath := range ImportRegistry { regKeys = append(regKeys, pkgPath) } for i := 1; i < len(regKeys); i++ { for j := i; j > 0 && regKeys[j] < regKeys[j-1]; j-- { regKeys[j], regKeys[j-1] = regKeys[j-1], regKeys[j] } } for _, pkgPath := range regKeys { ipkg := ImportRegistry[pkgPath] if ipkg == nil { continue } names := ipkg.Scope().Names() for ni := 0; ni < len(names); ni++ { tname := names[ni] tn3, ok7 := ipkg.Scope().Lookup(tname).(*types.TypeName) if !ok7 || tn3.Type() == nil { continue } named3, ok8 := tn3.Type().(*types.Named) if !ok8 { continue } for mi := 0; mi < named3.NumMethods(); mi++ { m := named3.Method(mi) if m.Name() != methodName { continue } isPR := m.HasPtrRecv() sym := pkgPath | "." | tname | "." | methodName tid := "" if isPR { tid = "reflect/types.type:pointer:named:" | pkgPath | "." | tname } else { tid = "reflect/types.type:named:" | pkgPath | "." | tname } impls = append(impls, ifaceImpl{ recvType: tn3.Type(), ptrRecv: isPR, extSymbol: sym, extTypeID: tid, }) } } } for i := 1; i < len(impls); i++ { for j := i; j > 0 && impls[j].recvType.String() < impls[j-1].recvType.String(); j-- { impls[j], impls[j-1] = impls[j-1], impls[j] } } return impls } func (e *irEmitter) findEmbedMethod(t syntax.Type, methodName string, depth int32) ([]int32, *ssa.SSAFunction, syntax.Type) { if depth > 5 { return nil, nil, nil } st, ok := types.SafeUnderlying(t).(*types.TCStruct) if !ok || st == nil { return nil, nil, nil } for fi := 0; fi < st.NumFields(); fi++ { f := st.Field(fi) if !f.Anonymous() { continue } embedType := f.Type() embedName := "" if en, ok2 := embedType.(*types.Named); ok2 && en.Obj() != nil { embedName = en.Obj().Name() } if embedName == "" { continue } embedMName := embedName | "." | methodName if fn, ok2 := e.pkg.Members[embedMName].(*ssa.SSAFunction); ok2 { return []int32{fi}, fn, embedType } sub, fn, embedT := e.findEmbedMethod(embedType, methodName, depth+1) if fn != nil { return append([]int32{fi}, sub...), fn, embedT } } return nil, nil, nil } type ifaceImpl struct { fn *ssa.SSAFunction recvType syntax.Type ptrRecv bool embedField int32 embedType syntax.Type embedChain []int32 extSymbol string extTypeID string } func (e *irEmitter) implFuncSym(impl ifaceImpl) string { if impl.extSymbol != "" { if irNeedsQuote(impl.extSymbol) { return "@\"" | impl.extSymbol | "\"" } return "@" | impl.extSymbol } return e.funcSymbol(impl.fn) } func (e *irEmitter) declareExtInvoke(impl ifaceImpl, inv *ssa.SSAInvoke) { if impl.extSymbol == "" { return } sym := e.implFuncSym(impl) if _, ok := e.extDecls[sym]; ok { return } retType := e.llvmType(inv.SSAType()) params := "ptr" for _, arg := range inv.Args { argT := e.llvmType(arg.SSAType()) if argT == "void" || argT == "" { argT = "ptr" } flat := flattenTypeStr(argT) if flat == "" { flat = "ptr" } params = params | ", " | flat } params = params | ", ptr" e.extDecls[sym] = retType | " " | sym | "(" | params | ")" } func flattenTypeStr(t string) string { if len(t) == 0 || t[0] != '{' { return t } result := "" depth := 0 start := 1 for i := 1; i < len(t)-1; i++ { if t[i] == '{' { depth++ } else if t[i] == '}' { depth-- } else if t[i] == ',' && depth == 0 { f := trimSpaces(t[start:i]) if f != "" { sub := flattenTypeStr(f) if result != "" { result = result | ", " | sub } else { result = sub } } start = i + 1 } } f := trimSpaces(t[start : len(t)-1]) if f != "" { sub := flattenTypeStr(f) if result != "" { result = result | ", " | sub } else { result = sub } } return result } func trimSpaces(s string) string { for len(s) > 0 && s[0] == ' ' { s = s[1:] } for len(s) > 0 && s[len(s)-1] == ' ' { s = s[:len(s)-1] } return s } func (e *irEmitter) emitExtInvokeCall(reg, retType, funcSym, recvLLVM, recv string, inv *ssa.SSAInvoke, isVoid bool) { extracts := "" callArgs := recvLLVM | " " | recv for _, arg := range inv.Args { argT := e.llvmType(arg.SSAType()) if argT == "void" { argT = "ptr" } argVal := e.operand(arg) if len(argT) > 0 && argT[0] == '{' { depth := 0 start := 1 fi := 0 for i := 1; i < len(argT)-1; i++ { if argT[i] == '{' { depth++ } else if argT[i] == '}' { depth-- } else if argT[i] == ',' && depth == 0 { ft := trimSpaces(argT[start:i]) if ft != "" { e.nextReg++ ex := "%ef" | irItoa(e.nextReg) extracts = extracts | " " | ex | " = extractvalue " | argT | " " | argVal | ", " | irItoa(fi) | "\n" callArgs = callArgs | ", " | ft | " " | ex fi++ } start = i + 1 } } ft := trimSpaces(argT[start : len(argT)-1]) if ft != "" { e.nextReg++ ex := "%ef" | irItoa(e.nextReg) extracts = extracts | " " | ex | " = extractvalue " | argT | " " | argVal | ", " | irItoa(fi) | "\n" callArgs = callArgs | ", " | ft | " " | ex } } else { callArgs = callArgs | ", " | argT | " " | argVal } } callArgs = callArgs | ", ptr null" e.w(extracts) e.w(" ") if !isVoid { e.w(reg) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(funcSym) ; e.w("(") e.w(callArgs) e.w(")\n") } func (e *irEmitter) implTypeID(impl ifaceImpl) string { if impl.extTypeID != "" { if e.extTypeIDs == nil { e.extTypeIDs = map[string]bool{} } quoted := "\"" | impl.extTypeID | "\"" e.extTypeIDs[quoted] = true return "@" | quoted } return e.typeIDGlobal(impl.recvType) } func (e *irEmitter) emitEmbedChainGEP(impl ifaceImpl, valPtr string) string { chain := impl.embedChain if len(chain) == 0 { chain = []int32{impl.embedField} } outerType := impl.recvType if pt, ok := outerType.(*types.Pointer); ok { outerType = pt.Elem() } cur := valPtr curType := outerType for _, idx := range chain { outerLLVM := e.llvmType(curType) gep := e.nextReg2("eg") e.w(" ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(outerLLVM) e.w(", ptr ") ; e.w(cur) ; e.w(", i32 0, i32 ") ; e.w(irItoa(idx)) ; e.w("\n") cur = gep st, ok := types.SafeUnderlying(curType).(*types.TCStruct) if ok && st != nil && idx < st.NumFields() { curType = st.Field(idx).Type() } } return cur } type invokeArg struct { typ string val string } func (e *irEmitter) prepareInvokeArgs(inv *ssa.SSAInvoke, impl ifaceImpl) []invokeArg { var sig *types.Signature if impl.fn != nil { sig = impl.fn.Signature } var result []invokeArg for i, arg := range inv.Args { argT := e.llvmType(arg.SSAType()) if at, ok := e.allocTypes[arg]; ok && at != "ptr" && at != "void" { argT = at } argV := e.operand(arg) if sig != nil && sig.Params() != nil && i < sig.Params().Len() { pt := e.llvmType(sig.Params().At(i).Type()) if pt != "void" && pt != "ptr" && pt != "" && argT != "void" && pt != argT && len(pt) > len(argT) { e.nextReg++ tmp := "%icast" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(pt) ; e.w("\n") e.w(" store ") ; e.w(pt) ; e.w(" zeroinitializer, ptr ") ; e.w(tmp) ; e.w("\n") e.w(" store ") ; e.w(argT) ; e.w(" ") ; e.w(argV) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.nextReg++ loaded := "%icld" | irItoa(e.nextReg) e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(pt) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") argT = pt argV = loaded } } if argT == "void" { argT = "ptr" } result = append(result, invokeArg{typ: argT, val: argV}) } return result } func (e *irEmitter) emitInvokeArgs(args []invokeArg) { for _, a := range args { e.w(", ") ; e.w(a.typ) ; e.w(" ") ; e.w(a.val) } } func (e *irEmitter) emitInvoke(inv *ssa.SSAInvoke) { reg := e.regName(inv) ifaceVal := e.operand(inv.X) retType := e.llvmType(inv.SSAType()) isVoid := retType == "void" tidPtr := e.nextReg2("tid") e.w(" ") ; e.w(tidPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(ifaceVal) ; e.w(", 0\n") valPtr := e.nextReg2("vp") e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(ifaceVal) ; e.w(", 1\n") impls := e.findIfaceImpls(inv.MethodName) for _, impl := range impls { e.declareExtInvoke(impl, inv) } if len(impls) == 0 { e.w(" ; invoke: no implementations for ") ; e.w(inv.MethodName) ; e.w("\n") if !isVoid { e.nextReg++ zp := "%zp" | irItoa(e.nextReg) e.w(" ") ; e.w(zp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n") e.w(" store ") ; e.w(retType) ; e.w(" zeroinitializer, ptr ") ; e.w(zp) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(zp) ; e.w("\n") } return } if len(impls) == 1 { impl := impls[0] callRecv := valPtr if impl.embedType != nil { callRecv = e.emitEmbedChainGEP(impl, valPtr) } var recvLLVM, recv string if impl.ptrRecv { recvLLVM = "ptr" recv = callRecv } else { if impl.embedType != nil { recvLLVM = e.llvmType(impl.embedType) } else { recvType := impl.recvType if pt, ok := recvType.(*types.Pointer); ok { recvType = pt.Elem() } recvLLVM = e.llvmType(recvType) } if recvLLVM == "ptr" { recv = callRecv } else if e.fitsInPtr(recvLLVM) { recv = e.extractScalarFromIface(callRecv, recvLLVM) } else { recv = e.nextReg2("ld") e.w(" ") ; e.w(recv) ; e.w(" = load ") ; e.w(recvLLVM) ; e.w(", ptr ") ; e.w(callRecv) ; e.w("\n") } } if impl.extSymbol != "" { e.emitExtInvokeCall(reg, retType, e.implFuncSym(impl), recvLLVM, recv, inv, isVoid) } else { e.w(" ") prepArgs := e.prepareInvokeArgs(inv, impl) if !isVoid { e.w(reg) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.implFuncSym(impl)) ; e.w("(") e.w(recvLLVM) ; e.w(" ") ; e.w(recv) e.emitInvokeArgs(prepArgs) e.w(", ptr null)\n") } return } baseID := e.nextReg mergeLabel := "invoke.merge" | irItoa(baseID) var checkLabels []string var caseLabels []string var callRegs []string for i := range impls { checkLabels = append(checkLabels, "invoke.check" | irItoa(baseID) | "." | irItoa(i)) caseLabels = append(caseLabels, "invoke.case" | irItoa(baseID) | "." | irItoa(i)) if !isVoid { callRegs = append(callRegs, e.nextReg2("cr")) } } defaultLabel := "invoke.default" | irItoa(baseID) e.w(" br label %") ; e.w(checkLabels[0]) ; e.w("\n") for i, impl := range impls { nextCheck := defaultLabel if i < len(impls)-1 { nextCheck = checkLabels[i+1] } e.w(checkLabels[i]) ; e.w(":\n") tidGlobal := e.implTypeID(impl) cmpReg := e.nextReg2("cmp") e.w(" ") ; e.w(cmpReg) ; e.w(" = icmp eq ptr ") ; e.w(tidPtr) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n") e.w(" br i1 ") ; e.w(cmpReg) ; e.w(", label %") ; e.w(caseLabels[i]) ; e.w(", label %") ; e.w(nextCheck) ; e.w("\n") e.w(caseLabels[i]) ; e.w(":\n") var recvLLVM, recv string callRecv := valPtr if impl.embedType != nil { callRecv = e.emitEmbedChainGEP(impl, valPtr) } if impl.ptrRecv { recvLLVM = "ptr" recv = callRecv } else { if impl.embedType != nil { recvLLVM = e.llvmType(impl.embedType) } else { recvType := impl.recvType if pt, ok := recvType.(*types.Pointer); ok { recvType = pt.Elem() } recvLLVM = e.llvmType(recvType) } if recvLLVM == "ptr" { recv = callRecv } else if e.fitsInPtr(recvLLVM) { recv = e.extractScalarFromIface(callRecv, recvLLVM) } else { recv = e.nextReg2("ld") e.w(" ") ; e.w(recv) ; e.w(" = load ") ; e.w(recvLLVM) ; e.w(", ptr ") ; e.w(callRecv) ; e.w("\n") } } if impl.extSymbol != "" { creg := "" if !isVoid { creg = callRegs[i] } e.emitExtInvokeCall(creg, retType, e.implFuncSym(impl), recvLLVM, recv, inv, isVoid) } else { prepArgs := e.prepareInvokeArgs(inv, impl) e.w(" ") if !isVoid { e.w(callRegs[i]) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.implFuncSym(impl)) ; e.w("(") e.w(recvLLVM) ; e.w(" ") ; e.w(recv) e.emitInvokeArgs(prepArgs) e.w(", ptr null)\n") } e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n") } e.w(defaultLabel) ; e.w(":\n") e.w(" unreachable\n") e.w(mergeLabel) ; e.w(":\n") if blk := inv.InstrBlock(); blk != nil { e.blockExitLabel[blk.Index] = "%" | mergeLabel } if !isVoid { e.w(" ") ; e.w(reg) ; e.w(" = phi ") ; e.w(retType) ; e.w(" ") for i := range impls { if i > 0 { e.w(", ") } e.w("[ ") ; e.w(callRegs[i]) ; e.w(", %") ; e.w(caseLabels[i]) ; e.w(" ]") } e.w("\n") } } func (e *irEmitter) emitTypeAssert(t *ssa.SSATypeAssert) { reg := e.regName(t) val := e.operand(t.X) assertedType := e.llvmType(t.AssertedType) voidAssert := assertedType == "void" if voidAssert { assertedType = "ptr" } // Check if input is already a concrete ptr (not an interface {ptr, ptr}) inputType := e.llvmType(t.X.SSAType()) if at, ok := e.allocTypes[t.X]; ok { inputType = at } var valPtr, typePtr string if inputType == "ptr" { valPtr = val typePtr = "null" } else { valPtr = e.nextReg2("ta") e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") typePtr = e.nextReg2("ta") e.w(" ") ; e.w(typePtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n") } if t.CommaOk { tidGlobal := e.typeIDGlobal(t.AssertedType) ok := e.nextReg2("ta") e.w(" ") ; e.w(ok) ; e.w(" = icmp eq ptr ") ; e.w(typePtr) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n") var loaded string if assertedType == "ptr" { loaded = e.nextReg2("ta") e.w(" ") ; e.w(loaded) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(valPtr) ; e.w(", ptr null\n") } else if e.fitsInPtr(assertedType) { loaded = e.extractScalarFromIface(valPtr, assertedType) } else { nonnull := e.nextReg2("ta") e.w(" ") ; e.w(nonnull) ; e.w(" = icmp ne ptr ") ; e.w(valPtr) ; e.w(", null\n") e.nextReg++ safeLabel := "ta.safe" | irItoa(e.nextReg) e.nextReg++ zeroLabel := "ta.zero" | irItoa(e.nextReg) e.nextReg++ mergeLabel := "ta.merge" | irItoa(e.nextReg) e.w(" br i1 ") ; e.w(nonnull) ; e.w(", label %") ; e.w(safeLabel) ; e.w(", label %") ; e.w(zeroLabel) ; e.w("\n") e.w(safeLabel) ; e.w(":\n") realLoad := e.nextReg2("ta") e.w(" ") ; e.w(realLoad) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n") e.w(zeroLabel) ; e.w(":\n") e.w(" br label %") ; e.w(mergeLabel) ; e.w("\n") e.w(mergeLabel) ; e.w(":\n") loaded = e.nextReg2("ta") e.w(" ") ; e.w(loaded) ; e.w(" = phi ") ; e.w(assertedType) ; e.w(" [ ") ; e.w(realLoad) ; e.w(", %") ; e.w(safeLabel) ; e.w(" ], [ zeroinitializer, %") ; e.w(zeroLabel) ; e.w(" ]\n") if blk := t.InstrBlock(); blk != nil { e.blockExitLabel[blk.Index] = "%" | mergeLabel } } tupType := "{" | assertedType | ", i1}" t1 := e.nextReg2("ta") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, ") ; e.w(assertedType) ; e.w(" ") ; e.w(loaded) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i1 ") ; e.w(ok) ; e.w(", 1\n") if voidAssert { e.allocTypes[t] = tupType } } else { if assertedType == "ptr" { e.w(" ") ; e.w(reg) ; e.w(" = select i1 true, ptr ") ; e.w(valPtr) ; e.w(", ptr null\n") } else if e.fitsInPtr(assertedType) { extracted := e.extractScalarFromIface(valPtr, assertedType) e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(assertedType) ; e.w(" ") ; e.w(extracted) ; e.w(", 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") } if voidAssert { e.allocTypes[t] = assertedType } } } func (e *irEmitter) extractScalarFromIface(valPtr string, assertedType string) string { ipt := e.intptrType() raw := e.nextReg2("ta") e.w(" ") ; e.w(raw) ; e.w(" = ptrtoint ptr ") ; e.w(valPtr) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") if assertedType == ipt { return raw } if assertedType == "i1" || assertedType == "i8" || assertedType == "i16" || assertedType == "i32" { tr := e.nextReg2("ta") e.w(" ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(raw) ; e.w(" to ") ; e.w(assertedType) ; e.w("\n") return tr } if assertedType == "float" { trDst := e.nextReg2("ta") src := e.emitIntCast(trDst, ipt, raw, "i32") bc := e.nextReg2("ta") e.w(" ") ; e.w(bc) ; e.w(" = bitcast i32 ") ; e.w(src) ; e.w(" to float\n") return bc } if assertedType == "double" { bc := e.nextReg2("ta") e.w(" ") ; e.w(bc) ; e.w(" = bitcast ") ; e.w(ipt) ; e.w(" ") ; e.w(raw) ; e.w(" to double\n") return bc } return raw } func (e *irEmitter) emitMakeMap(m *ssa.SSAMakeMap) { reg := e.regName(m) ipt := e.intptrType() var mt *types.TCMap if okv, okok := types.SafeUnderlying(m.SSAType()).(*types.TCMap); okok { mt = okv } keyType := "i32" valType := "i32" alg := "0" if mt != nil { keyType = e.llvmType(mt.Key()) valType = e.llvmType(mt.Elem()) if e.isStringLike(mt.Key()) { alg = "1" } } keySz := e.nextReg2("mm") e.w(" ") ; e.w(keySz) ; e.w(" = ptrtoint ptr getelementptr (") e.w(keyType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") valSz := e.nextReg2("mm") e.w(" ") ; e.w(valSz) ; e.w(" = ptrtoint ptr getelementptr (") e.w(valType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") hint := "8" if m.Reserve != nil { hint = e.operand(m.Reserve) } e.w(" ") ; e.w(reg) ; e.w(" = call ptr @runtime.hashmapMake(") e.w(ipt) ; e.w(" ") ; e.w(keySz) ; e.w(", ") e.w(ipt) ; e.w(" ") ; e.w(valSz) ; e.w(", ") e.w(ipt) ; e.w(" ") ; e.w(hint) ; e.w(", i8 ") ; e.w(alg) ; e.w(")\n") e.declareRuntime("runtime.hashmapMake", "ptr", ipt | ", " | ipt | ", " | ipt | ", i8") e.scopeTrackAlloc(reg) } func (e *irEmitter) emitMapUpdate(m *ssa.SSAMapUpdate) { mapVal := e.operand(m.Map) keyVal := e.operand(m.Key) valVal := e.operand(m.Value) mapType := m.Map.SSAType() if pt, ok := types.SafeUnderlying(mapType).(*types.Pointer); ok { mapType = pt.Elem() } var mt *types.TCMap if okv, okok := types.SafeUnderlying(mapType).(*types.TCMap); okok { mt = okv } keyType := "i32" valType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) valType = e.llvmType(mt.Elem()) } if keyVal == "null" && keyType != "ptr" { keyVal = "zeroinitializer" } if valVal == "null" && valType != "ptr" { valVal = "zeroinitializer" } keyAlloca := e.nextReg2("mu") e.w(" ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n") e.w(" store ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n") valAlloca := e.nextReg2("mu") e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") actualValType := e.llvmType(m.Value.SSAType()) resolved := e.resolvedType(m.Value, actualValType) if resolved != actualValType { actualValType = resolved } if actualValType != valType && valType == "{ptr, ptr}" && actualValType == "ptr" { e.w(" store {ptr, ptr} zeroinitializer, ptr ") ; e.w(valAlloca) ; e.w("\n") vfld := e.nextReg2("mu") e.w(" ") ; e.w(vfld) ; e.w(" = getelementptr inbounds {ptr, ptr}, ptr ") ; e.w(valAlloca) ; e.w(", i32 0, i32 1\n") e.w(" store ptr ") ; e.w(valVal) ; e.w(", ptr ") ; e.w(vfld) ; e.w("\n") } else { e.w(" store ") ; e.w(valType) ; e.w(" ") ; e.w(valVal) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") } if mt != nil && e.isStringLike(mt.Key()) { sty := e.sliceType() ipt := e.intptrType() kd := e.nextReg2("mu") kl := e.nextReg2("mu") kc := e.nextReg2("mu") e.w(" ") ; e.w(kd) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 0\n") e.w(" ") ; e.w(kl) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 1\n") e.w(" ") ; e.w(kc) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 2\n") e.w(" call void @runtime.hashmapContentSet(ptr ") ; e.w(mapVal) e.w(", ptr ") ; e.w(kd) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(kl) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(kc) e.w(", ptr ") ; e.w(valAlloca) ; e.w(")\n") e.declareRuntime("runtime.hashmapContentSet", "void", "ptr, ptr, " | ipt | ", " | ipt | ", ptr") } else { e.w(" call void @runtime.hashmapBinarySet(ptr ") ; e.w(mapVal) e.w(", ptr ") ; e.w(keyAlloca) e.w(", ptr ") ; e.w(valAlloca) ; e.w(")\n") e.declareRuntime("runtime.hashmapBinarySet", "void", "ptr, ptr, ptr") } } func (e *irEmitter) emitLookup(l *ssa.SSALookup) { reg := e.regName(l) ipt := e.intptrType() mapVal := e.operand(l.X) keyVal := e.operand(l.Index) var mt *types.TCMap if okv, okok := types.SafeUnderlying(l.X.SSAType()).(*types.TCMap); okok { mt = okv } keyType := "i32" valType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) valType = e.llvmType(mt.Elem()) } valAlloca := e.nextReg2("ml") e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") valSz := e.nextReg2("ml") e.w(" ") ; e.w(valSz) ; e.w(" = ptrtoint ptr getelementptr (") e.w(valType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") if mt != nil && e.isStringLike(mt.Key()) { sty := e.sliceType() kd := e.nextReg2("ml") kl := e.nextReg2("ml") kc := e.nextReg2("ml") e.w(" ") ; e.w(kd) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 0\n") e.w(" ") ; e.w(kl) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 1\n") e.w(" ") ; e.w(kc) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(keyVal) ; e.w(", 2\n") okReg := e.nextReg2("ml") e.w(" ") ; e.w(okReg) ; e.w(" = call i1 @runtime.hashmapContentGet(ptr ") ; e.w(mapVal) e.w(", ptr ") ; e.w(kd) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(kl) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(kc) e.w(", ptr ") ; e.w(valAlloca) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(valSz) ; e.w(")\n") e.declareRuntime("runtime.hashmapContentGet", "i1", "ptr, ptr, " | ipt | ", " | ipt | ", ptr, " | ipt) if l.CommaOk { loaded := e.nextReg2("ml") e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(valType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") tupType := "{" | valType | ", i1}" t1 := e.nextReg2("ml") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, ") ; e.w(valType) ; e.w(" ") ; e.w(loaded) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i1 ") ; e.w(okReg) ; e.w(", 1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(valType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") } } else { keyAlloca := e.nextReg2("ml") e.w(" ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n") e.w(" store ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n") okReg := e.nextReg2("ml") e.w(" ") ; e.w(okReg) ; e.w(" = call i1 @runtime.hashmapBinaryGet(ptr ") ; e.w(mapVal) e.w(", ptr ") ; e.w(keyAlloca) e.w(", ptr ") ; e.w(valAlloca) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(valSz) ; e.w(")\n") e.declareRuntime("runtime.hashmapBinaryGet", "i1", "ptr, ptr, ptr, " | ipt) if l.CommaOk { loaded := e.nextReg2("ml") e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(valType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") tupType := "{" | valType | ", i1}" t1 := e.nextReg2("ml") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, ") ; e.w(valType) ; e.w(" ") ; e.w(loaded) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i1 ") ; e.w(okReg) ; e.w(", 1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(valType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") } } } func (e *irEmitter) isStringLike(t syntax.Type) bool { if t == nil { return false } if b, ok := types.SafeUnderlying(t).(*types.Basic); ok { return b.Info()&types.IsString != 0 } return false } func (e *irEmitter) emitMakeClosure(m *ssa.SSAMakeClosure) { reg := e.regName(m) var fn *ssa.SSAFunction if okv, okok := m.Fn.(*ssa.SSAFunction); okok { fn = okv } ipt := e.intptrType() if len(m.Bindings) == 0 { t1 := e.nextReg2("mc") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue {ptr, ptr} undef, ptr null, 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} ") ; e.w(t1) e.w(", ptr ") ; e.w(e.funcSymbol(fn)) ; e.w(", 1\n") e.declareExternalFunc(fn) return } ctxType := e.closureContextType(m.Bindings) ctxSz := e.nextReg2("mc") e.w(" ") ; e.w(ctxSz) ; e.w(" = ptrtoint ptr getelementptr (") e.w(ctxType) ; e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") ctxPtr := e.nextReg2("mc") e.w(" ") ; e.w(ctxPtr) ; e.w(" = call ptr @runtime.alloc(") e.w(ipt) ; e.w(" ") ; e.w(ctxSz) ; e.w(", ptr null, ptr undef)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") e.scopeTrackAlloc(ctxPtr) for i, b := range m.Bindings { bval := e.operand(b) bt := e.closureBindingType(b) gep := e.nextReg2("mc") e.w(" ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(ctxType) ; e.w(", ptr ") e.w(ctxPtr) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" store ") ; e.w(bt) ; e.w(" ") ; e.w(bval) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") _ = b } t1 := e.nextReg2("mc") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue {ptr, ptr} undef, ptr ") ; e.w(ctxPtr) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} ") ; e.w(t1) e.w(", ptr ") ; e.w(e.funcSymbol(fn)) ; e.w(", 1\n") e.declareExternalFunc(fn) } func (e *irEmitter) closureBindingType(b ssa.SSAValue) string { return "ptr" } func (e *irEmitter) closureContextType(bindings []ssa.SSAValue) string { s := "{" for i, b := range bindings { if i > 0 { s = s | ", " } s = s | e.closureBindingType(b) } return s | "}" } func (e *irEmitter) freeVarType(fv *ssa.SSAFreeVar) string { return "ptr" } func (e *irEmitter) emitFreeVarUnpack(f *ssa.SSAFunction) { ctxName := "context" for _, p := range f.Params { if p.SSAName() == "context" { ctxName = "context.1" break } } ctxType := "{" for i, fv := range f.FreeVars { if i > 0 { ctxType = ctxType | ", " } ctxType = ctxType | e.freeVarType(fv) } ctxType = ctxType | "}" for i, fv := range f.FreeVars { fvName := e.regName(fv) e.nextReg++ gep := "%fv" | irItoa(e.nextReg) e.w(" ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(ctxType) e.w(", ptr %") ; e.w(ctxName) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" ") ; e.w(fvName) ; e.w(" = load ptr, ptr ") ; e.w(gep) ; e.w("\n") } } func (e *irEmitter) emitPanic(p *ssa.SSAPanic) { if c, ok := p.X.(*ssa.SSAConst); ok && e.isStringLike(c.SSAType()) { arg := e.operand(c) sty := e.sliceType() e.w(" call void @runtime._panicstr(") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(")\n") e.declareRuntime("runtime._panicstr", "void", sty) e.w(" unreachable\n") return } e.w(" call void @runtime._panic(ptr null)\n") e.declareRuntime("runtime._panic", "void", "ptr") e.w(" unreachable\n") } func (e *irEmitter) emitRange(r *ssa.SSARange) { reg := e.regName(r) if _, ok := types.SafeUnderlying(r.X.SSAType()).(*types.TCMap); ok { e.w(" ") ; e.w(reg) ; e.w(" = alloca [48 x i8]\n") e.w(" call void @llvm.memset.p0.i64(ptr ") ; e.w(reg) ; e.w(", i8 0, i64 48, i1 false)\n") e.declareRuntime("llvm.memset.p0.i64", "void", "ptr, i8, i64, i1") return } ipt := e.intptrType() e.w(" ") ; e.w(reg) ; e.w(" = alloca ") ; e.w(ipt) ; e.w("\n") e.w(" store ") ; e.w(ipt) ; e.w(" 0, ptr ") ; e.w(reg) ; e.w("\n") } func (e *irEmitter) emitNext(n *ssa.SSANext) { reg := e.regName(n) rangeInstr := n.Iter.(*ssa.SSARange) iterPtr := e.regName(rangeInstr) collVal := e.operand(rangeInstr.X) if mt, ok := types.SafeUnderlying(rangeInstr.X.SSAType()).(*types.TCMap); ok { e.emitNextMap(reg, iterPtr, collVal, mt, n) return } if arr, ok := types.SafeUnderlying(rangeInstr.X.SSAType()).(*types.Array); ok { e.emitNextArray(reg, iterPtr, collVal, arr, n) return } if p, ok := types.SafeUnderlying(rangeInstr.X.SSAType()).(*types.Pointer); ok && p.Elem() != nil { if arr, ok2 := types.SafeUnderlying(p.Elem()).(*types.Array); ok2 { e.emitNextPtrArray(reg, iterPtr, collVal, arr, n) return } } collLLVM := e.llvmType(rangeInstr.X.SSAType()) if len(collLLVM) > 0 && collLLVM[0] == 'i' { tupType := e.llvmType(n.SSAType()) if at, ok := e.allocTypes[n]; ok { tupType = at } e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" zeroinitializer, i1 false, 0\n") return } e.emitNextSlice(reg, iterPtr, collVal, rangeInstr, n) } func (e *irEmitter) emitNextSlice(reg, iterPtr, collVal string, rangeInstr *ssa.SSARange, n *ssa.SSANext) { ipt := e.intptrType() sty := e.sliceType() idx := e.nextReg2("rn") e.w(" ") ; e.w(idx) ; e.w(" = load ") ; e.w(ipt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") slLen := e.nextReg2("rn") e.w(" ") ; e.w(slLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(collVal) ; e.w(", 1\n") ok := e.nextReg2("rn") e.w(" ") ; e.w(ok) ; e.w(" = icmp ult ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", ") ; e.w(slLen) ; e.w("\n") keyDst := e.nextReg2("rn") key := e.emitIntCast(keyDst, ipt, idx, "i32") dataPtr := e.nextReg2("rn") e.w(" ") ; e.w(dataPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(collVal) ; e.w(", 0\n") elemType := "i32" elemResolved := false xType := rangeInstr.X.SSAType() if sl, ok2 := types.SafeUnderlying(xType).(*types.Slice); ok2 { elemType = e.llvmType(sl.Elem()) elemResolved = true } else if xType != nil { switch t := xType.(type) { case *types.Slice: elemType = e.llvmType(t.Elem()) elemResolved = true case *types.Basic: elemType = "i8" elemResolved = true case *types.Named: if sl2, ok3 := t.Underlying().(*types.Slice); ok3 { elemType = e.llvmType(sl2.Elem()) elemResolved = true } else if e.isStringLike(t) { elemType = "i8" elemResolved = true } } } if !elemResolved && elemType == "i32" { if tup, ok2 := n.SSAType().(*types.Tuple); ok2 && tup.Len() >= 3 { vt := tup.At(2).Type() if vt != nil { vlt := e.llvmType(vt) if vlt != "void" { elemType = vlt elemResolved = true } } } } if !elemResolved && elemType == "i32" && sty == e.sliceType() { elemType = "i8" } eptr := e.nextReg2("rn") e.w(" ") ; e.w(eptr) ; e.w(" = getelementptr ") ; e.w(elemType) e.w(", ptr ") ; e.w(dataPtr) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") fallback := e.nextReg2("rn") e.w(" ") ; e.w(fallback) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" zeroinitializer, ptr ") ; e.w(fallback) ; e.w("\n") safePtr := e.nextReg2("rn") e.w(" ") ; e.w(safePtr) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(eptr) e.w(", ptr ") ; e.w(fallback) ; e.w("\n") elem := e.nextReg2("rn") e.w(" ") ; e.w(elem) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(safePtr) ; e.w("\n") inc := e.nextReg2("rn") e.w(" ") ; e.w(inc) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", 1\n") newCnt := e.nextReg2("rn") e.w(" ") ; e.w(newCnt) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(inc) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") e.w(" store ") ; e.w(ipt) ; e.w(" ") ; e.w(newCnt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") tupType := "{i1, i32, " | elemType | "}" e.allocTypes[n] = tupType t1 := e.nextReg2("rn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := e.nextReg2("rn") e.w(" ") ; e.w(t2) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i32 ") ; e.w(key) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t2) ; e.w(", ") ; e.w(elemType) ; e.w(" ") ; e.w(elem) ; e.w(", 2\n") } func (e *irEmitter) emitNextArray(reg, iterPtr, collVal string, arr *types.Array, n *ssa.SSANext) { ipt := e.intptrType() arrLen := arr.Len() if arrLen < 0 { rangeInstr := n.Iter.(*ssa.SSARange) e.emitNextSlice(reg, iterPtr, collVal, rangeInstr, n) return } elemType := e.llvmType(arr.Elem()) arrType := "[" | irItoa(int32(arrLen)) | " x " | elemType | "]" idx := e.nextReg2("rn") e.w(" ") ; e.w(idx) ; e.w(" = load ") ; e.w(ipt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") ok := e.nextReg2("rn") e.w(" ") ; e.w(ok) ; e.w(" = icmp ult ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", ") ; e.w(irItoa(int32(arrLen))) ; e.w("\n") keyDst2 := e.nextReg2("rn") key := e.emitIntCast(keyDst2, ipt, idx, "i32") // Store array to memory to get element pointer via GEP arrAlloca := e.nextReg2("rn") e.w(" ") ; e.w(arrAlloca) ; e.w(" = alloca ") ; e.w(arrType) ; e.w("\n") e.w(" store ") ; e.w(arrType) ; e.w(" ") ; e.w(collVal) ; e.w(", ptr ") ; e.w(arrAlloca) ; e.w("\n") eptr := e.nextReg2("rn") e.w(" ") ; e.w(eptr) ; e.w(" = getelementptr inbounds ") ; e.w(arrType) e.w(", ptr ") ; e.w(arrAlloca) ; e.w(", i32 0, ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") fallback := e.nextReg2("rn") e.w(" ") ; e.w(fallback) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" zeroinitializer, ptr ") ; e.w(fallback) ; e.w("\n") safePtr := e.nextReg2("rn") e.w(" ") ; e.w(safePtr) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(eptr) e.w(", ptr ") ; e.w(fallback) ; e.w("\n") elem := e.nextReg2("rn") e.w(" ") ; e.w(elem) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(safePtr) ; e.w("\n") inc := e.nextReg2("rn") e.w(" ") ; e.w(inc) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", 1\n") newCnt := e.nextReg2("rn") e.w(" ") ; e.w(newCnt) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(inc) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") e.w(" store ") ; e.w(ipt) ; e.w(" ") ; e.w(newCnt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") tupType := "{i1, i32, " | elemType | "}" e.allocTypes[n] = tupType t1 := e.nextReg2("rn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := e.nextReg2("rn") e.w(" ") ; e.w(t2) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i32 ") ; e.w(key) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t2) ; e.w(", ") ; e.w(elemType) ; e.w(" ") ; e.w(elem) ; e.w(", 2\n") } func (e *irEmitter) emitNextPtrArray(reg, iterPtr, collVal string, arr *types.Array, n *ssa.SSANext) { ipt := e.intptrType() arrLen := arr.Len() elemType := e.llvmType(arr.Elem()) arrType := "[" | irItoa(int32(arrLen)) | " x " | elemType | "]" idx := e.nextReg2("rn") e.w(" ") ; e.w(idx) ; e.w(" = load ") ; e.w(ipt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") ok := e.nextReg2("rn") e.w(" ") ; e.w(ok) ; e.w(" = icmp ult ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", ") ; e.w(irItoa(int32(arrLen))) ; e.w("\n") keyDst := e.nextReg2("rn") key := e.emitIntCast(keyDst, ipt, idx, "i32") eptr := e.nextReg2("rn") e.w(" ") ; e.w(eptr) ; e.w(" = getelementptr inbounds ") ; e.w(arrType) e.w(", ptr ") ; e.w(collVal) ; e.w(", i32 0, ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") fallback := e.nextReg2("rn") e.w(" ") ; e.w(fallback) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" zeroinitializer, ptr ") ; e.w(fallback) ; e.w("\n") safePtr := e.nextReg2("rn") e.w(" ") ; e.w(safePtr) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ptr ") ; e.w(eptr) e.w(", ptr ") ; e.w(fallback) ; e.w("\n") elem := e.nextReg2("rn") e.w(" ") ; e.w(elem) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(safePtr) ; e.w("\n") inc := e.nextReg2("rn") e.w(" ") ; e.w(inc) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", 1\n") newCnt := e.nextReg2("rn") e.w(" ") ; e.w(newCnt) ; e.w(" = select i1 ") ; e.w(ok) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(inc) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w("\n") e.w(" store ") ; e.w(ipt) ; e.w(" ") ; e.w(newCnt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") tupType := "{i1, i32, " | elemType | "}" e.allocTypes[n] = tupType t1 := e.nextReg2("rn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := e.nextReg2("rn") e.w(" ") ; e.w(t2) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", i32 ") ; e.w(key) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t2) ; e.w(", ") ; e.w(elemType) ; e.w(" ") ; e.w(elem) ; e.w(", 2\n") } func (e *irEmitter) emitNextMap(reg, iterPtr, collVal string, mt *types.TCMap, n *ssa.SSANext) { keyType := e.llvmType(mt.Key()) valType := e.llvmType(mt.Elem()) keyAlloca := e.nextReg2("mn") e.w(" ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n") valAlloca := e.nextReg2("mn") e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") ok := e.nextReg2("mn") e.w(" ") ; e.w(ok) ; e.w(" = call i1 @runtime.hashmapNext(ptr ") ; e.w(collVal) e.w(", ptr ") ; e.w(iterPtr) e.w(", ptr ") ; e.w(keyAlloca) e.w(", ptr ") ; e.w(valAlloca) ; e.w(")\n") key := e.nextReg2("mn") e.w(" ") ; e.w(key) ; e.w(" = load ") ; e.w(keyType) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n") val := e.nextReg2("mn") e.w(" ") ; e.w(val) ; e.w(" = load ") ; e.w(valType) ; e.w(", ptr ") ; e.w(valAlloca) ; e.w("\n") tupType := e.llvmType(n.SSAType()) t1 := e.nextReg2("mn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := e.nextReg2("mn") e.w(" ") ; e.w(t2) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t1) ; e.w(", ") ; e.w(keyType) ; e.w(" ") ; e.w(key) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" ") ; e.w(t2) ; e.w(", ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(", 2\n") e.declareRuntime("runtime.hashmapNext", "i1", "ptr, ptr, ptr, ptr") }