package main import ( "bytes" ) var parseErrors []string var genericFuncDecls map[string]*FuncDecl var genericPkgScopes map[string]*Scope type irEmitter struct { buf []byte triple string ptrBits int pkg *SSAPackage valName map[SSAValue]string nextReg int extDecls map[string]string extGlobals map[string]string strConst []string strMap map[string]int curFunc *SSAFunction typeIDs map[string]int typeIDNext int extTypeIDs map[string]bool localTypeIDs map[string]bool allocTypes map[SSAValue]string regTypes map[string]string hoisted map[SSAValue]bool blockExitLabel map[int]string nameUsed map[string]bool missingStores map[SSAValue]SSAValue globalTypes map[string]string globalDeclTypes map[string]string sortedMembers []SSAMember loadToGlobal map[string]*SSAGlobal allocBlock map[SSAValue]int storedTo map[string]bool usedAs map[string]bool } func sortedKeys(m map[string]bool) []string { var keys []string for k := range m { keys = append(keys, k) } for i := 1; i < len(keys); i++ { for j := i; j > 0 && keys[j] < keys[j-1]; j-- { keys[j], keys[j-1] = keys[j-1], keys[j] } } return keys } func newIREmitter(pkg *SSAPackage, triple string) *irEmitter { ptrBits := 64 if len(triple) >= 4 && triple[:4] == "wasm" { ptrBits = 32 } return &irEmitter{ buf: []byte{:0:4096}, triple: triple, ptrBits: ptrBits, pkg: pkg, valName: map[SSAValue]string{}, extDecls: map[string]string{}, extGlobals: map[string]string{}, strMap: map[string]int{}, allocTypes: map[SSAValue]string{}, regTypes: map[string]string{}, blockExitLabel: map[int]string{}, nameUsed: map[string]bool{}, } } func (e *irEmitter) dataLayout() string { if len(e.triple) >= 6 && e.triple[:6] == "x86_64" { return "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" } if len(e.triple) >= 7 && e.triple[:7] == "aarch64" { return "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" } if len(e.triple) >= 6 && e.triple[:6] == "wasm32" { return "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" } if len(e.triple) >= 3 && e.triple[:3] == "arm" { return "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" } return "" } func (e *irEmitter) w(s string) { e.buf = append(e.buf, s...) } func (e *irEmitter) regName(v SSAValue) string { if v == nil { e.nextReg++ return "%r" | irItoa(e.nextReg) } if n, ok := e.valName[v]; ok { return n } name := v.SSAName() if name == "" { e.nextReg++ name = "r" | irItoa(e.nextReg) } n := "%" | name for e.nameUsed[n] { e.nextReg++ n = "%r" | irItoa(e.nextReg) } e.valName[v] = n e.nameUsed[n] = true return n } func (e *irEmitter) setRegType(v SSAValue, reg string, typ string) { e.allocTypes[v] = typ if len(reg) > 0 && reg[0] == '%' { e.regTypes[reg] = typ } } func (e *irEmitter) resolvedType(v SSAValue, fallback string) string { if at, ok := e.allocTypes[v]; ok { return at } if n, ok := e.valName[v]; ok { if rt, ok2 := e.regTypes[n]; ok2 { return rt } } op := e.operandNoSideEffect(v) if len(op) > 0 && op[0] == '%' { if rt, ok := e.regTypes[op]; ok { return rt } } return fallback } func (e *irEmitter) llvmType(t Type) string { if t == nil { return "void" } u := safeUnderlying(t) if u == nil { if _, ok := t.(*Slice); ok { return e.sliceType() } if n, ok := t.(*Named); ok { _ = n return "ptr" } return "ptr" } t = u switch t := t.(type) { case *Basic: return e.llvmBasicType(t) case *Pointer: return "ptr" case *Slice: return e.sliceType() case *Array: n := t.Len() if n < 0 { return e.sliceType() } elem := e.llvmType(t.Elem()) return "[" | irItoa(int(n)) | " x " | elem | "]" case *TCStruct: return e.llvmStructType(t) case *Signature: return "{ptr, ptr}" case *TCMap: return "ptr" case *TCChan: return "ptr" case *TCInterface: return e.ifaceType() case *Named: if t.obj != nil && t.obj.Name() == "error" { return e.ifaceType() } if t.NumMethods() > 0 { return e.ifaceType() } return "ptr" case *Tuple: if t.Len() == 0 { return "void" } if t.Len() == 1 { return e.llvmType(t.At(0).Type()) } s := "{" for i := 0; i < t.Len(); i++ { if i > 0 { s = s | ", " } ft := e.llvmType(t.At(i).Type()) if ft == "void" { ft = "ptr" } s = s | ft } return s | "}" } return "i8" } func (e *irEmitter) llvmBasicType(t *Basic) string { switch t.Kind() { case Bool: return "i1" case Int8, Uint8: return "i8" case Int16, Uint16: return "i16" case Int32, Uint32: return "i32" case Int64, Uint64: return "i64" case Float32: return "float" case Float64: return "double" case TCString: return e.sliceType() case UnsafePointer: return "ptr" case UntypedBool: return "i1" case UntypedInt, UntypedRune: return "i32" case UntypedFloat: return "double" case UntypedString: return e.sliceType() } return "i32" } func (e *irEmitter) ptrType() string { return "ptr" } func (e *irEmitter) intptrType() string { if e.ptrBits == 32 { return "i32" } return "i64" } func (e *irEmitter) sliceType() string { ipt := e.intptrType() return "{ptr, " | ipt | ", " | ipt | "}" } func (e *irEmitter) ifaceType() string { return "{ptr, ptr}" } func (e *irEmitter) nextReg2(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } func (e *irEmitter) llvmStructType(t *TCStruct) string { s := "{" for i := 0; i < t.NumFields(); i++ { if i > 0 { s = s | ", " } ft := e.llvmType(t.Field(i).Type()) if ft == "void" { ft = "ptr" } s = s | ft } return s | "}" } func (e *irEmitter) declareRuntime(name, retType, params string) { e.extDecls[name] = retType | " @" | name | "(" | params | ")" } func (e *irEmitter) declareExternalGlobal(g *SSAGlobal) { if g.pkg == nil || g.pkg == e.pkg { return } name := e.globalName(g) if _, ok := e.extGlobals[name]; ok { return } typ := e.llvmType(g.typ) if p, ok := safeUnderlying(g.typ).(*Pointer); ok { typ = e.llvmType(p.Elem()) } e.extGlobals[name] = typ } func (e *irEmitter) declareExternalFunc(fn *SSAFunction) { sym := e.funcSymbol(fn) if _, ok := e.extDecls[sym]; ok { return } retType := e.funcRetType(fn) params := "" hasRecv := fn.Signature != nil && fn.Signature.Recv() != nil if hasRecv { params = "ptr" } if fn.Signature != nil && fn.Signature.Params() != nil { for i := 0; i < fn.Signature.Params().Len(); i++ { if params != "" { params = params | ", " } params = params | e.llvmType(fn.Signature.Params().At(i).Type()) } } if !fn.isExternC { if params != "" { params = params | ", " } params = params | "ptr" } e.extDecls[sym] = retType | " " | sym | "(" | params | ")" } func (e *irEmitter) addStringConst(s string) int { if idx, ok := e.strMap[s]; ok { return idx } idx := len(e.strConst) e.strConst = append(e.strConst, s) e.strMap[s] = idx return idx } func (e *irEmitter) strConstGlobal(idx int) string { return "@.str." | irItoa(idx) } func irEscapeString(s string) string { var buf []byte for i := 0; i < len(s); i++ { c := s[i] if c >= 32 && c < 127 && c != '\\' && c != '"' { buf = append(buf, c) } else { buf = append(buf, '\\') buf = append(buf, "0123456789ABCDEF"[c>>4]) buf = append(buf, "0123456789ABCDEF"[c&0xf]) } } return string(buf) } func (e *irEmitter) emit() string { dl := e.dataLayout() if dl != "" { e.w("target datalayout = \"") e.w(dl) e.w("\"\n") } e.w("target triple = \"") e.w(e.triple) e.w("\"\n\n") e.globalTypes = map[string]string{} e.globalDeclTypes = map[string]string{} for _, member := range e.pkgMembersSorted() { fn, ok := member.(*SSAFunction) if !ok { continue } for _, b := range fn.Blocks { for _, instr := range b.Instrs { if s, ok2 := instr.(*SSAStore); ok2 && s.Addr != nil && s.Val != nil { if g, ok3 := s.Addr.(*SSAGlobal); ok3 { vt := e.llvmType(s.Val.SSAType()) if vt != "ptr" && vt != "void" && vt != "i1" && vt != "" { name := e.globalName(g) gt := "" if p, ok4 := safeUnderlying(g.typ).(*Pointer); ok4 { gt = e.llvmType(p.Elem()) } if gt != "" && gt != "ptr" && gt != "i8" && gt[0] == '{' && vt[0] != '{' { vt = gt } e.globalTypes[name] = vt } } } } } if e.loadToGlobal == nil { e.loadToGlobal = map[string]*SSAGlobal{} } else { for k := range e.loadToGlobal { delete(e.loadToGlobal, k) } } for _, b := range fn.Blocks { for _, instr := range b.Instrs { load, ok2 := instr.(*SSAUnOp) if !ok2 || load.Op != OpMul { continue } g, ok3 := load.X.(*SSAGlobal) if !ok3 { continue } e.loadToGlobal[load.SSAName()] = g } } for _, b := range fn.Blocks { for _, instr := range b.Instrs { if ret, ok2 := instr.(*SSAReturn); ok2 { if fn.Signature == nil || fn.Signature.Results() == nil { continue } for i, res := range ret.Results { if i >= fn.Signature.Results().Len() { break } if g, ok3 := e.loadToGlobal[res.SSAName()]; ok3 { expectType := e.llvmType(fn.Signature.Results().At(i).Type()) if expectType != "ptr" && expectType != "void" && expectType != "i1" && expectType != "" { name := e.globalName(g) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = expectType } } } } } call, ok2 := instr.(*SSACall) if !ok2 { continue } callee := call.Call.Value if callee == nil { continue } var sig *Signature if cfn, ok3 := callee.(*SSAFunction); ok3 && cfn.Signature != nil { sig = cfn.Signature } else { if okv, okok := safeUnderlying(callee.SSAType()).(*Signature); okok { sig = okv } } if sig == nil || sig.Params() == nil { continue } recvOff := 0 if sig.Recv() != nil { recvOff = 1 } for i, arg := range call.Call.Args { if arg == nil { continue } sigIdx := i - recvOff if sigIdx < 0 || sigIdx >= sig.Params().Len() { continue } g, found := e.loadToGlobal[arg.SSAName()] if !found { continue } expectType := e.llvmType(sig.Params().At(sigIdx).Type()) name := e.globalName(g) if expectType != "void" && expectType != "i1" && expectType != "" { if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = expectType } } } } } for _, b := range fn.Blocks { for _, instr := range b.Instrs { if rng, ok2 := instr.(*SSARange); ok2 && rng.X != nil { if g, ok3 := e.loadToGlobal[rng.X.SSAName()]; ok3 { name := e.globalName(g) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = "ptr" } } } if mu, ok2 := instr.(*SSAMapUpdate); ok2 && mu.Map != nil { if g, ok3 := e.loadToGlobal[mu.Map.SSAName()]; ok3 { name := e.globalName(g) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = "ptr" } } } if lu, ok2 := instr.(*SSALookup); ok2 && lu.X != nil { if g, ok3 := e.loadToGlobal[lu.X.SSAName()]; ok3 { name := e.globalName(g) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = "ptr" } } } bop, ok2 := instr.(*SSABinOp) if !ok2 { continue } if bop.X == nil || bop.Y == nil { continue } gx, xIsGlobal := e.loadToGlobal[bop.X.SSAName()] gy, yIsGlobal := e.loadToGlobal[bop.Y.SSAName()] if xIsGlobal { yt := e.llvmType(bop.Y.SSAType()) if yt != "ptr" && yt != "void" && yt != "i1" && yt != "" { name := e.globalName(gx) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = yt } } } if yIsGlobal { xt := e.llvmType(bop.X.SSAType()) if xt != "ptr" && xt != "void" && xt != "i1" && xt != "" { name := e.globalName(gy) if _, exists := e.globalTypes[name]; !exists { e.globalTypes[name] = xt } } } } } } for _, member := range e.pkgMembersSorted() { switch m := member.(type) { case *SSAGlobal: if m.name != "_" { e.emitGlobal(m) } } } for _, member := range e.pkgMembersSorted() { switch m := member.(type) { case *SSAFunction: e.emitFunction(m) e.emitAnonFuncs(m) m.Blocks = nil m.Locals = nil m.Params = nil m.FreeVars = nil m.NamedResults = nil m.vars = nil } } e.emitLibMain() for i, s := range e.strConst { e.w(e.strConstGlobal(i)) e.w(" = private constant [") e.w(irItoa(len(s))) e.w(" x i8] c\"") e.w(irEscapeString(s)) e.w("\"\n") } var tidKeys []string for name := range e.typeIDs { tidKeys = append(tidKeys, name) } for i := 1; i < len(tidKeys); i++ { for j := i; j > 0 && tidKeys[j] < tidKeys[j-1]; j-- { tidKeys[j], tidKeys[j-1] = tidKeys[j-1], tidKeys[j] } } for _, name := range tidKeys { if hasPrefix(name, "reflect/types.type:") { quoted := "\"" | name | "\"" if e.extTypeIDs != nil { if _, dup := e.extTypeIDs[quoted]; dup { continue } } if e.localTypeIDs != nil && e.localTypeIDs[quoted] { e.w("@\"") e.w(name) e.w("\" = global i8 0\n") } else { e.w("@\"") e.w(name) e.w("\" = external global i8\n") } } else { e.w("@") e.w(name) e.w(" = private constant i32 0\n") } } if len(e.extDecls) > 0 { e.w("\n") var edKeys []string for k := range e.extDecls { edKeys = append(edKeys, k) } for i := 1; i < len(edKeys); i++ { for j := i; j > 0 && edKeys[j] < edKeys[j-1]; j-- { edKeys[j], edKeys[j-1] = edKeys[j-1], edKeys[j] } } for _, k := range edKeys { decl := e.extDecls[k] if decl == "" { continue } e.w("declare ") e.w(decl) e.w("\n") } } if len(e.extGlobals) > 0 { e.w("\n") var egKeys []string for name := range e.extGlobals { egKeys = append(egKeys, name) } for i := 1; i < len(egKeys); i++ { for j := i; j > 0 && egKeys[j] < egKeys[j-1]; j-- { egKeys[j], egKeys[j-1] = egKeys[j-1], egKeys[j] } } for _, name := range egKeys { e.w(name) e.w(" = external global ") e.w(e.extGlobals[name]) e.w("\n") } } if len(e.extTypeIDs) > 0 { e.w("\n") for _, tid := range sortedKeys(e.extTypeIDs) { e.w("@") ; e.w(tid) ; e.w(" = external global i8\n") } } return string(e.buf) } func (e *irEmitter) releaseAfterEmit() { e.buf = nil e.valName = nil e.extDecls = nil e.extGlobals = nil e.strMap = nil e.strConst = nil e.typeIDs = nil e.extTypeIDs = nil e.localTypeIDs = nil e.allocTypes = nil e.regTypes = nil e.hoisted = nil e.blockExitLabel = nil e.nameUsed = nil e.missingStores = nil e.globalTypes = nil e.globalDeclTypes = nil e.sortedMembers = nil e.loadToGlobal = nil e.allocBlock = nil e.storedTo = nil e.usedAs = nil e.pkg = nil e.curFunc = nil } func (e *irEmitter) pkgMembersSorted() []SSAMember { if e.sortedMembers != nil { return e.sortedMembers } var members []SSAMember for _, m := range e.pkg.Members { members = append(members, m) } for i := 1; i < len(members); i++ { for j := i; j > 0 && members[j].MemberName() < members[j-1].MemberName(); j-- { members[j], members[j-1] = members[j-1], members[j] } } e.sortedMembers = members return members } func (e *irEmitter) inferGlobalTypeFromLoads(g *SSAGlobal) string { gname := g.SSAName() for _, member := range e.pkgMembersSorted() { fn, ok := member.(*SSAFunction) if !ok { continue } for _, b := range fn.Blocks { for _, instr := range b.Instrs { if load, ok2 := instr.(*SSAUnOp); ok2 && load.Op == OpMul { if lg, ok3 := load.X.(*SSAGlobal); ok3 && lg.SSAName() == gname { lt := e.llvmType(load.SSAType()) if lt != "void" && lt != "i8" && lt != "ptr" { return lt } } } } } } return "" } func (e *irEmitter) resolveGlobalDeclType(g *SSAGlobal) string { name := e.globalName(g) if dt, ok := e.globalDeclTypes[name]; ok { return dt } typ := e.llvmType(g.typ) gtu := safeUnderlying(g.typ) elemNil := false if p, ok := gtu.(*Pointer); ok { pElem := p.Elem() if pElem == nil { elemNil = true typ = e.ifaceType() } else { typ = e.llvmType(pElem) } } if !elemNil && (typ == "ptr" || typ == "i8") { if gt, ok := e.globalTypes[name]; ok && gt != "ptr" && gt != "void" && gt != "" { typ = gt } } if typ == "void" { typ = "i1" } e.globalDeclTypes[name] = typ return typ } func (e *irEmitter) emitGlobal(g *SSAGlobal) { name := e.globalName(g) typ := e.resolveGlobalDeclType(g) e.w(name) e.w(" = global ") e.w(typ) e.w(" zeroinitializer\n") } func (e *irEmitter) globalName(g *SSAGlobal) string { pkg := e.pkg.Pkg.Path() if g.pkg != nil { pkg = g.pkg.Pkg.Path() } return irGlobalSymbol(pkg, g.name) } func irNeedsQuote(s string) bool { for i := 0; i < len(s); i++ { c := s[i] if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '$' { continue } return true } return false } func irGlobalSymbol(pkg, name string) string { sym := pkg | "." | name if irNeedsQuote(sym) { return "@\"" | sym | "\"" } return "@" | sym } func (e *irEmitter) funcSymbol(f *SSAFunction) string { if f.externalSymbol != "" { sym := f.externalSymbol if irNeedsQuote(sym) { return "@\"" | sym | "\"" } return "@" | sym } pkg := e.pkg.Pkg.Path() if f.Pkg != nil { pkg = f.Pkg.Pkg.Path() } return irGlobalSymbol(pkg, f.name) } func (e *irEmitter) isPkgFunc(f *SSAFunction) bool { if f.Pkg == e.pkg { return true } if f.parent != nil { return e.isPkgFunc(f.parent) } return false } func (e *irEmitter) emitAnonFuncs(f *SSAFunction) { for _, af := range f.AnonFuncs { e.emitFunction(af) e.emitAnonFuncs(af) af.Blocks = nil af.Locals = nil af.Params = nil af.FreeVars = nil af.NamedResults = nil af.vars = nil } f.AnonFuncs = nil } func (e *irEmitter) emitLibMain() { pkgPath := e.pkg.Pkg.Path() if pkgPath == "main" { return } hasMain := false for _, m := range e.pkg.Members { if fn, ok := m.(*SSAFunction); ok && fn.name == "main" { hasMain = true break } } if hasMain { return } sym := irGlobalSymbol(pkgPath, "main") e.w("\ndefine void ") e.w(sym) e.w("(ptr %context) {\nentry:\n ret void\n}\n") } func (e *irEmitter) emitFunction(f *SSAFunction) { e.w("; [emit] " | f.name | "\n") if len(f.Blocks) == 0 { e.emitFuncDecl(f) return } e.curFunc = f e.nextReg = 0 for k := range e.valName { delete(e.valName, k) } for k := range e.nameUsed { delete(e.nameUsed, k) } for k := range e.allocTypes { delete(e.allocTypes, k) } for k := range e.regTypes { delete(e.regTypes, k) } for k := range e.blockExitLabel { delete(e.blockExitLabel, k) } usedNames := map[string]int{} for i, p := range f.Params { pname := p.SSAName() if pname == "" { pname = "p" | irItoa(i) } if cnt, ok := usedNames[pname]; ok { pname = pname | "." | irItoa(cnt) } usedNames[p.SSAName()]++ e.valName[p] = "%" | pname } e.w("\ndefine ") if f.parent != nil { e.w("hidden ") } e.w(e.funcRetType(f)) e.w(" ") e.w(e.funcSymbol(f)) e.w("(") for i, p := range f.Params { if i > 0 { e.w(", ") } pt := e.llvmType(p.SSAType()) if pt == "void" { pt = "ptr" } e.w(pt) e.w(" ") e.w(e.regName(p)) } if !f.isExternC { if len(f.Params) > 0 { e.w(", ") } ctxName := "context" for _, p := range f.Params { if p.SSAName() == "context" { ctxName = "context.1" break } } e.w("ptr %") e.w(ctxName) } e.w(") {\n") // Pre-scan: set allocTypes, detect cross-block alloca references if e.allocBlock == nil { e.allocBlock = map[SSAValue]int{} } else { for k := range e.allocBlock { delete(e.allocBlock, k) } } for _, b := range f.Blocks { for _, instr := range b.Instrs { if n, ok := instr.(*SSANext); ok { if ri, ok2 := n.Iter.(*SSARange); ok2 { if arr, ok3 := safeUnderlying(ri.X.SSAType()).(*Array); ok3 { elemType := e.llvmType(arr.Elem()) e.allocTypes[n] = "{i1, i32, " | elemType | "}" } else if sl, ok3 := safeUnderlying(ri.X.SSAType()).(*Slice); ok3 { elemType := e.llvmType(sl.Elem()) e.allocTypes[n] = "{i1, i32, " | elemType | "}" } } } if c, ok := instr.(*SSACall); ok { if b2, ok2 := c.Call.Value.(*SSABuiltin); ok2 && b2.SSAName() == "recover" { e.allocTypes[c] = e.ifaceType() } } if a, ok := instr.(*SSAAlloc); ok { e.allocBlock[a] = b.Index } } } hoistAllocs := map[SSAValue]bool{} for _, b := range f.Blocks { for _, instr := range b.Instrs { refs := e.instrOperands(instr) for _, ref := range refs { if ab, ok := e.allocBlock[ref]; ok && ab != 0 && ab != b.Index { hoistAllocs[ref] = true } } } } e.hoisted = hoistAllocs e.missingStores = nil if e.storedTo == nil { e.storedTo = map[string]bool{} } else { for k := range e.storedTo { delete(e.storedTo, k) } } for _, b := range f.Blocks { for _, instr := range b.Instrs { if s, ok := instr.(*SSAStore); ok && s.Addr != nil { e.storedTo[s.Addr.SSAName()] = true } } } if e.usedAs == nil { e.usedAs = map[string]bool{} } else { for k := range e.usedAs { delete(e.usedAs, k) } } for _, b := range f.Blocks { for _, instr := range b.Instrs { refs := e.instrOperands(instr) for _, ref := range refs { if ref != nil { e.usedAs[ref.SSAName()] = true } } } } for _, b := range f.Blocks { for i := 0; i+1 < len(b.Instrs); i++ { load, isLoad := b.Instrs[i].(*SSAUnOp) if !isLoad || load.Op != OpMul { continue } alloc, isAlloc := b.Instrs[i+1].(*SSAAlloc) if !isAlloc { continue } if !e.usedAs[load.SSAName()] && !e.storedTo[alloc.SSAName()] && hoistAllocs[alloc] { srcAlloc, isSrcAlloc := load.X.(*SSAAlloc) if !isSrcAlloc { continue } srcType := e.llvmType(srcAlloc.SSAType()) if p, ok := safeUnderlying(srcAlloc.SSAType()).(*Pointer); ok && p.Elem() != nil { srcType = e.llvmType(p.Elem()) } if len(srcType) > 0 && srcType[0] == '[' { if e.missingStores == nil { e.missingStores = map[SSAValue]SSAValue{} } e.missingStores[load] = alloc e.allocTypes[alloc] = srcType } } } } var hoistList []*SSAAlloc for v := range hoistAllocs { if a, ok := v.(*SSAAlloc); ok { hoistList = append(hoistList, a) } } for i := 1; i < len(hoistList); i++ { for j := i; j > 0 && hoistList[j].SSAName() < hoistList[j-1].SSAName(); j-- { hoistList[j], hoistList[j-1] = hoistList[j-1], hoistList[j] } } for _, b := range f.Blocks { if b.Index == 0 { e.w("entry:\n") for _, a := range hoistList { e.emitAlloc(a) } if len(e.curFunc.FreeVars) > 0 { e.emitFreeVarUnpack(e.curFunc) } for _, instr := range b.Instrs { e.emitInstr(instr) } } else { e.emitBlock(b) } } e.hoisted = nil e.w("}\n") } func (e *irEmitter) emitFuncDecl(f *SSAFunction) { sym := e.funcSymbol(f) if _, ok := e.extDecls[sym]; ok { return } e.w("\ndeclare ") e.w(e.funcRetType(f)) e.w(" ") e.w(sym) e.w("(") n := 0 hasRecv := f.Signature != nil && f.Signature.Recv() != nil if hasRecv { e.w("ptr") n++ } if f.Signature != nil && f.Signature.Params() != nil { for i := 0; i < f.Signature.Params().Len(); i++ { if n > 0 { e.w(", ") } e.w(e.llvmType(f.Signature.Params().At(i).Type())) n++ } } if !f.isExternC { if n > 0 { e.w(", ") } e.w("ptr") } e.w(")\n") e.extDecls[sym] = "" } func (e *irEmitter) resolveResultType(t Type) string { rt := e.llvmType(t) if rt != "void" { return rt } if t == nil { return "ptr" } u := safeUnderlying(t) if u == nil { return "ptr" } switch u.(type) { case *TCInterface: return e.ifaceType() case *Signature: return "{ptr, ptr}" case *TCStruct: return e.llvmStructType(u.(*TCStruct)) case *Slice: return e.sliceType() } return "ptr" } func (e *irEmitter) funcRetType(f *SSAFunction) string { if f.Signature == nil || f.Signature.Results() == nil || f.Signature.Results().Len() == 0 { return "void" } if f.Signature.Results().Len() == 1 { return e.resolveResultType(f.Signature.Results().At(0).Type()) } s := "{" for i := 0; i < f.Signature.Results().Len(); i++ { if i > 0 { s = s | ", " } s = s | e.resolveResultType(f.Signature.Results().At(i).Type()) } return s | "}" } func (e *irEmitter) emitBlock(b *SSABasicBlock) { label := "b" | irItoa(b.Index) if b.Index == 0 { label = "entry" } e.w(label) e.w(":\n") e.blockExitLabel[b.Index] = "%" | label if b.Index == 0 && len(e.curFunc.FreeVars) > 0 { e.emitFreeVarUnpack(e.curFunc) } for _, instr := range b.Instrs { e.emitInstr(instr) if e.missingStores != nil { if v, ok2 := instr.(SSAValue); ok2 { if dst, ok3 := e.missingStores[v]; ok3 { loadReg := e.regName(v) dstReg := e.regName(dst) arrType := e.allocTypes[dst] e.w(" store ") ; e.w(arrType) ; e.w(" ") ; e.w(loadReg) ; e.w(", ptr ") ; e.w(dstReg) ; e.w("\n") } } } } hasTerminator := false if n := len(b.Instrs); n > 0 { switch b.Instrs[n-1].(type) { case *SSAJump, *SSAIf, *SSAReturn: hasTerminator = true } } if !hasTerminator { e.w(" unreachable\n") } } func (e *irEmitter) blockLabel(b *SSABasicBlock) string { if b.Index == 0 { return "%entry" } return "%b" | irItoa(b.Index) } func (e *irEmitter) emitInstr(instr SSAInstruction) { switch i := instr.(type) { case *SSAAlloc: if e.hoisted != nil && e.hoisted[i] { break } e.emitAlloc(i) case *SSAStore: e.emitStore(i) case *SSABinOp: e.emitBinOp(i) case *SSAUnOp: e.emitUnOp(i) case *SSACall: e.emitCall(i) case *SSAPhi: e.emitPhi(i) case *SSAReturn: e.emitReturn(i) case *SSAJump: e.emitJump(i) case *SSAIf: e.emitIf(i) case *SSAConvert: e.emitConvert(i) case *SSAChangeType: e.emitChangeType(i) case *SSAFieldAddr: e.emitFieldAddr(i) case *SSAIndexAddr: e.emitIndexAddr(i) case *SSAExtract: e.emitExtract(i) case *SSAMakeSlice: e.emitMakeSlice(i) case *SSASlice: e.emitSliceOp(i) case *SSAMakeInterface: e.emitMakeInterface(i) case *SSAInvoke: e.emitInvoke(i) case *SSATypeAssert: e.emitTypeAssert(i) case *SSAMakeMap: e.emitMakeMap(i) case *SSAMapUpdate: e.emitMapUpdate(i) case *SSALookup: e.emitLookup(i) case *SSAMakeClosure: e.emitMakeClosure(i) case *SSAPanic: e.emitPanic(i) case *SSARunDefers: e.w(" ; rundefers\n") case *SSADefer: e.w(" ; defer\n") case *SSASend: e.w(" ; send\n") case *SSAGo: e.w(" ; go\n") case *SSASelect: e.w(" ; select\n") case *SSARange: e.emitRange(i) case *SSANext: e.emitNext(i) case *SSAMakeChan: e.w(" ; makechan\n") } } func (e *irEmitter) emitAlloc(a *SSAAlloc) { reg := e.regName(a) if at, ok := e.allocTypes[a]; ok && len(at) > 0 && at[0] == '[' { if a.Heap { ipt := e.intptrType() e.nextReg++ sz := "%ha" | irItoa(e.nextReg) e.w(" ") ; e.w(sz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(at) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") e.w(" ") ; e.w(reg) e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") } else { e.w(" ") ; e.w(reg) ; e.w(" = alloca ") ; e.w(at) ; e.w("\n") e.w(" store ") ; e.w(at) ; e.w(" zeroinitializer, ptr ") ; e.w(reg) ; e.w("\n") } return } elemType := e.llvmType(a.SSAType()) nilElem := false if p, ok := safeUnderlying(a.SSAType()).(*Pointer); ok { if p.Elem() != nil { elemType = e.llvmType(p.Elem()) } else { nilElem = true } } isDoublePtr := false if p, ok := safeUnderlying(a.SSAType()).(*Pointer); ok && p.Elem() != nil { if _, ok2 := safeUnderlying(p.Elem()).(*Pointer); ok2 { isDoublePtr = true } } if isDoublePtr && elemType == "ptr" { e.allocTypes[a] = elemType } else if elemType == "void" || (elemType == "ptr" && nilElem) { inferred := e.inferAllocTypeFromStores(a) if inferred != "ptr" || elemType == "void" { elemType = inferred } e.allocTypes[a] = elemType } else { override := e.inferAllocTypeFromStores(a) if override != "ptr" && override != elemType { bothScalar := len(elemType) > 0 && elemType[0] == 'i' && len(override) > 0 && override[0] == 'i' isFloatToInt := (elemType == "double" || elemType == "float") && len(override) > 0 && override[0] == 'i' isScalarToAgg := len(elemType) > 0 && (elemType[0] == 'i' || elemType == "double" || elemType == "float") && len(override) > 0 && override[0] == '{' if !bothScalar && !isFloatToInt && !isScalarToAgg { elemType = override e.allocTypes[a] = elemType } } if elemType == "i32" && override == "ptr" { usage := e.inferAllocTypeFromUsage(a) if usage != "ptr" && usage != "i32" { elemType = usage e.allocTypes[a] = elemType } } } if !isDoublePtr { if faType := e.inferAllocTypeFromFieldAddrs(a, elemType); faType != "" { retType := e.inferAllocTypeFromReturn(a) callType := e.inferAllocTypeFromCallArgs(a) appendType := e.inferAllocTypeFromAppendUsage(a) best := faType if retType != "" && len(retType) > len(best) { best = retType } if callType != "" && len(callType) > len(best) { best = callType } if appendType != "" && len(appendType) > len(best) { best = appendType } if elemType != best { elemType = best e.allocTypes[a] = elemType } } } if a.Heap { ipt := e.intptrType() e.nextReg++ sz := "%ha" | irItoa(e.nextReg) e.w(" ") ; e.w(sz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") e.w(" ") ; e.w(reg) e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") } else { e.w(" ") e.w(reg) e.w(" = alloca ") e.w(elemType) e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" zeroinitializer, ptr ") ; e.w(reg) ; e.w("\n") } } func (e *irEmitter) inferAllocTypeFromStores(a *SSAAlloc) string { allocName := a.SSAName() for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if s, ok := instr.(*SSAStore); ok && s.Addr != nil && s.Addr.SSAName() == allocName { if at, ok2 := e.allocTypes[s.Val]; ok2 && at != "ptr" && at != "void" { return at } vt := e.llvmType(s.Val.SSAType()) if vt != "void" && vt != "" { return vt } if call, ok := s.Val.(*SSACall); ok { if b2, ok2 := call.Call.Value.(*SSABuiltin); ok2 && b2.SSAName() == "append" { return e.sliceType() } } if _, ok := s.Val.(*SSASlice); ok { return e.sliceType() } if _, ok := s.Val.(*SSAMakeSlice); ok { return e.sliceType() } } } } return "ptr" } func (e *irEmitter) inferAllocTypeFromReturn(a *SSAAlloc) string { allocName := a.SSAName() for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { ret, ok := instr.(*SSAReturn) if !ok { continue } for i, rv := range ret.Results { if rv == nil { continue } if uop, ok2 := rv.(*SSAUnOp); ok2 && uop.Op == OpMul && uop.X != nil && uop.X.SSAName() == allocName { sig := e.curFunc.Signature if sig != nil && sig.Results() != nil && i < sig.Results().Len() { rt := e.llvmType(sig.Results().At(i).Type()) if rt != "void" && rt != "ptr" && rt != "" { return rt } } return "" } } } } return "" } func (e *irEmitter) inferAllocTypeFromCallArgs(a *SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*SSAUnOp); ok && uop.Op == OpMul && uop.X != nil && uop.X.SSAName() == allocName { loadNames[uop.SSAName()] = true } } } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { call, ok := instr.(*SSACall) if !ok { continue } callee := call.Call.Value if callee == nil { continue } var sig *Signature if cfn, ok2 := callee.(*SSAFunction); ok2 && cfn.Signature != nil { sig = cfn.Signature } else { if okv, okok := safeUnderlying(callee.SSAType()).(*Signature); okok { sig = okv } } if sig == nil || sig.Params() == nil { continue } recvOff := 0 if sig.Recv() != nil { recvOff = 1 } for i, arg := range call.Call.Args { if arg == nil { continue } if !loadNames[arg.SSAName()] { continue } sigIdx := i - recvOff if sigIdx < 0 || sigIdx >= sig.Params().Len() { continue } pt := e.llvmType(sig.Params().At(sigIdx).Type()) if pt != "void" && pt != "ptr" && pt != "" && len(pt) > 0 && pt[0] == '{' { return pt } } } } return "" } func (e *irEmitter) inferAllocTypeFromAppendUsage(a *SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*SSAUnOp); ok && uop.Op == OpMul && uop.X != nil && uop.X.SSAName() == allocName { loadNames[uop.SSAName()] = true } } } if len(loadNames) == 0 { return "" } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { call, ok := instr.(*SSACall) if !ok { continue } bi, ok2 := call.Call.Value.(*SSABuiltin) if !ok2 || bi.SSAName() != "append" { continue } if len(call.Call.Args) < 2 { continue } for j := 1; j < len(call.Call.Args); j++ { arg := call.Call.Args[j] if arg == nil { continue } if !loadNames[arg.SSAName()] { continue } sliceArg := call.Call.Args[0] if sl, ok3 := safeUnderlying(sliceArg.SSAType()).(*Slice); ok3 { et := e.llvmType(sl.Elem()) if et != "" && et != "void" && et != "ptr" && len(et) > 0 && et[0] == '{' { return et } } if sl, ok3 := sliceArg.SSAType().(*Slice); ok3 { et := e.llvmType(sl.Elem()) if et != "" && et != "void" && et != "ptr" && len(et) > 0 && et[0] == '{' { return et } } } } } return "" } func (e *irEmitter) inferAllocTypeFromFieldAddrs(a *SSAAlloc, baseType string) string { allocName := a.SSAName() names := map[string]bool{allocName: true} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*SSAUnOp); ok && uop.Op == OpMul && uop.X != nil && uop.X.SSAName() == allocName { names[uop.SSAName()] = true } } } maxField := -1 fieldTypes := map[int]string{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { fa, ok := instr.(*SSAFieldAddr) if !ok || fa.X == nil || !names[fa.X.SSAName()] { continue } if fa.Field > maxField { maxField = fa.Field } faName := fa.SSAName() for _, b2 := range e.curFunc.Blocks { for _, i2 := range b2.Instrs { if s, ok2 := i2.(*SSAStore); ok2 && s.Addr != nil && s.Addr.SSAName() == faName { ft := e.llvmType(s.Val.SSAType()) if ft != "void" && ft != "" { fieldTypes[fa.Field] = ft } } if ld, ok2 := i2.(*SSAUnOp); ok2 && ld.Op == OpMul && ld.X != nil && ld.X.SSAName() == faName { ft := e.llvmType(ld.SSAType()) if ft != "void" && ft != "" && ft != "ptr" { if _, exists := fieldTypes[fa.Field]; !exists { fieldTypes[fa.Field] = ft } } } } } } } if maxField < 0 { return "" } baseFields := parseStructFields(baseType) top := maxField if len(baseFields)-1 > top { top = len(baseFields) - 1 } s := "{" for i := 0; i <= top; i++ { if i > 0 { s = s | ", " } ft, ok := fieldTypes[i] if !ok { if i < len(baseFields) && baseFields[i] != "" { ft = baseFields[i] } else { ft = "ptr" } } else if i < len(baseFields) && baseFields[i] != "" { bw := irParseIntWidth(baseFields[i]) fw := irParseIntWidth(ft) if bw > 0 && fw > 0 && bw > fw { ft = baseFields[i] } } s = s | ft } return s | "}" } func parseStructFields(s string) []string { if len(s) < 2 || s[0] != '{' || s[len(s)-1] != '}' { return nil } inner := s[1 : len(s)-1] var fields []string depth := 0 start := int32(0) for i := int32(0); i < int32(len(inner)); i++ { switch inner[i] { case '{': depth++ case '}': depth-- case ',': if depth == 0 { f := llvmTrimSpace(string(inner[start:i])) fields = append(fields, f) start = i + 1 } } } f := llvmTrimSpace(string(inner[start:])) if f != "" { fields = append(fields, f) } return fields } func llvmTrimSpace(s string) string { i := int32(0) for i < int32(len(s)) && s[i] == ' ' { i++ } j := int32(len(s)) for j > i && s[j-1] == ' ' { j-- } return string(s[i:j]) } func (e *irEmitter) inferAllocTypeFromUsage(a *SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { load, ok := instr.(*SSAUnOp) if !ok || load.Op != OpMul { continue } if load.X != nil && load.X.SSAName() == allocName { loadNames[load.SSAName()] = true } } } if len(loadNames) == 0 { return "ptr" } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { switch u := instr.(type) { case *SSASlice: if u.X != nil && loadNames[u.X.SSAName()] { return e.sliceType() } case *SSAIndexAddr: if u.X != nil && loadNames[u.X.SSAName()] { return e.sliceType() } case *SSACall: for _, arg := range u.Call.Args { if arg != nil && loadNames[arg.SSAName()] { if bi, ok2 := u.Call.Value.(*SSABuiltin); ok2 { nm := bi.SSAName() if nm == "append" || nm == "copy" || nm == "len" || nm == "cap" { return e.sliceType() } } } } } } } return "ptr" } func (e *irEmitter) emitStore(s *SSAStore) { if s.Val == nil || s.Addr == nil { e.w(" ; store with nil val/addr\n") return } valType := e.llvmType(s.Val.SSAType()) val := e.operand(s.Val) if load, ok := s.Val.(*SSAUnOp); ok && load.Op == OpMul { if g, ok2 := load.X.(*SSAGlobal); ok2 { valType = e.resolveGlobalDeclType(g) } } if _, isAlloc := s.Val.(*SSAAlloc); !isAlloc { if at, ok := e.allocTypes[s.Val]; ok && at != valType { bothScalar := len(valType) > 0 && valType[0] == 'i' && len(at) > 0 && at[0] == 'i' if !bothScalar { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } } if len(valType) > 0 && (valType[0] == '[' || valType[0] == '{') { if addrAt, ok := e.allocTypes[s.Addr]; ok && addrAt != valType { if len(valType) >= len(addrAt) || (valType[0] == '[' && addrAt[0] == '{') { e.allocTypes[s.Addr] = valType } } } if valType == "void" { if at, ok := e.allocTypes[s.Addr]; ok && at != "ptr" && at != "void" { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } else if valType == "ptr" { if uop, ok := s.Val.(*SSAUnOp); ok && uop.Op == OpMul { if at, ok2 := e.allocTypes[s.Addr]; ok2 && at != "ptr" && at != "void" { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } } if valType == "void" { if _, isFV := s.Addr.(*SSAFreeVar); isFV { valType = e.llvmType(s.Addr.SSAType()) } else if p, ok := safeUnderlying(s.Addr.SSAType()).(*Pointer); ok { valType = e.llvmType(p.Elem()) } if valType == "void" { valType = "ptr" } if val == "null" && valType != "ptr" { val = "zeroinitializer" } } addr := e.operand(s.Addr) if at, ok := e.allocTypes[s.Addr]; ok && (at == "double" || at == "float") && len(valType) > 0 && valType[0] == 'i' { if isConstOperand(val) { val = ensureFloatLit(val) } else { e.nextReg++ conv := "%si2f" | irItoa(e.nextReg) e.w(" ") ; e.w(conv) ; e.w(" = sitofp ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(at) ; e.w("\n") val = conv } valType = at } if at, ok2 := e.allocTypes[s.Addr]; ok2 && len(at) > 0 && at[0] == '{' && len(valType) > 0 && valType[0] == 'i' { if val == "0" || val == "zeroinitializer" { val = "zeroinitializer" valType = at } } if at, ok2 := e.allocTypes[s.Addr]; ok2 && len(at) > 0 && at[0] == 'i' && len(valType) > 0 && valType[0] == '{' { valType = at val = "zeroinitializer" } if p, ok := safeUnderlying(s.Addr.SSAType()).(*Pointer); ok { elemT := e.llvmType(p.Elem()) if len(elemT) > 1 && elemT[0] == 'i' && len(valType) > 1 && valType[0] == 'i' && elemT != valType { vw := irParseIntWidth(valType) ew := irParseIntWidth(elemT) if ew > 0 && vw > ew { e.nextReg++ trunc := "%tr" | irItoa(e.nextReg) e.w(" ") e.w(trunc) e.w(" = trunc ") e.w(valType) e.w(" ") e.w(val) e.w(" to ") e.w(elemT) e.w("\n") val = trunc valType = elemT } else if ew > 0 && vw > 0 && vw < ew { e.nextReg++ ext := "%se" | irItoa(e.nextReg) e.w(" ") e.w(ext) e.w(" = sext ") e.w(valType) e.w(" ") e.w(val) e.w(" to ") e.w(elemT) e.w("\n") val = ext valType = elemT } } } e.w(" store ") e.w(valType) e.w(" ") e.w(val) e.w(", ptr ") e.w(addr) e.w("\n") } func (e *irEmitter) emitZeroReg(reg string, typ Type) { rt := e.llvmType(typ) if rt == "void" || rt == "" { rt = "i32" } if rt == "ptr" { e.w(" ") ; e.w(reg) ; e.w(" = inttoptr i64 0 to ptr\n") } else if rt == "i1" { e.w(" ") ; e.w(reg) ; e.w(" = add i1 false, false\n") } else if e.intBits(rt) > 0 { e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(rt) ; e.w(" 0, 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = add i32 0, 0\n") } } func (e *irEmitter) emitBinOp(b *SSABinOp) { if b.X == nil || b.X.SSAType() == nil { recov := false if b.X == nil { for _, blk := range e.curFunc.Blocks { for i, inst := range blk.Instrs { if inst == b && i > 0 { if prev, ok := blk.Instrs[i-1].(*SSAUnOp); ok && prev.Op == OpMul { b.X = prev recov = true } } } if recov { break } } } if !recov && b.X != nil { rt := e.resolvedType(b.X, "i32") if rt != "i32" && rt != "" { reg := e.regName(b) lv := e.operand(b.X) rv := e.operand(b.Y) op := e.llvmBinOp(b.Op, nil) e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(op) ; e.w(" ") ; e.w(rt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(rv) ; e.w("\n") e.setRegType(b, reg, rt) return } } if !recov { e.emitZeroReg(e.regName(b), b.SSAType()) return } } reg := e.regName(b) lt := e.llvmType(b.X.SSAType()) if lt == "void" && b.Y != nil { lt = e.llvmType(b.Y.SSAType()) } if at, ok := e.allocTypes[b.X]; ok && at != "ptr" && at != "void" && at != lt { isScalarToAgg := len(lt) > 0 && (lt[0] == 'i' || lt == "double" || lt == "float") && len(at) > 0 && at[0] == '{' if !isScalarToAgg { lt = at } } lv := e.operand(b.X) rv := e.operand(b.Y) if (b.Op == OpAdd || b.Op == OpOr) && b.X.SSAType() != nil { if sl, ok := safeUnderlying(b.X.SSAType()).(*Slice); ok { e.emitSliceConcat(reg, sl, lv, rv) return } if e.isStringLike(b.X.SSAType()) { e.emitSliceConcat(reg, NewSlice(Typ[Uint8]), lv, rv) return } } if b.X.SSAType() != nil && e.isStringLike(b.X.SSAType()) && (lt == e.sliceType() || lt == "ptr" || lt == "void") { isActuallyIface := false if at, ok := e.allocTypes[b.X]; ok && at == e.ifaceType() { isActuallyIface = true } rvOK := true if b.Y != nil { rvType := e.llvmType(b.Y.SSAType()) rvResolved := e.resolvedType(b.Y, rvType) if load, ok := b.Y.(*SSAUnOp); ok && load.Op == OpMul { if g, ok2 := load.X.(*SSAGlobal); ok2 { if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 { rvResolved = gt } } } if e.intBits(rvResolved) > 0 { rvOK = false } } if !isActuallyIface && rvOK { e.emitStringCompare(reg, b.Op, lv, rv) return } } if lt == e.sliceType() { rvOK2 := true if b.Y != nil { if load, ok := b.Y.(*SSAUnOp); ok && load.Op == OpMul { if g, ok2 := load.X.(*SSAGlobal); ok2 { if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 && e.intBits(gt) > 0 { rvOK2 = false } } } } if rvOK2 { e.emitStringCompare(reg, b.Op, lv, rv) return } } if (b.Op == OpEql || b.Op == OpNeq) && (rv == "null" || rv == "zeroinitializer" || lv == "null" || lv == "zeroinitializer") && b.X.SSAType() != nil { u := safeUnderlying(b.X.SSAType()) _, isIface := u.(*TCInterface) _, isSlice := u.(*Slice) _, isSig := u.(*Signature) _, isPtr := u.(*Pointer) _, isMap := u.(*TCMap) _, isChan := u.(*TCChan) if isMap || isChan { isPtr = true } if !isIface && !isSlice && !isSig && !isPtr && u == nil && (lt == "{ptr, ptr}" || lt == "{ptr, i64}") { isIface = true } if isSig { e.nextReg++ extReg := "%ne" | irItoa(e.nextReg) aggVal := lv if lv == "null" || lv == "zeroinitializer" { aggVal = rv } e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(aggVal) ; e.w(", 1\n") cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", null\n") return } if isIface || isSlice || e.isStringLike(b.X.SSAType()) { e.nextReg++ extReg := "%ne" | irItoa(e.nextReg) aggVal := lv if lv == "null" || lv == "zeroinitializer" { aggVal = rv } e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(aggVal) ; e.w(", 0\n") cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", null\n") return } if isPtr { cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } ptrVal := lv if lv == "null" || lv == "zeroinitializer" { ptrVal = rv } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(ptrVal) ; e.w(", null\n") return } } if (b.Op == OpEql || b.Op == OpNeq) && b.X.SSAType() != nil { if st, ok := safeUnderlying(b.X.SSAType()).(*TCStruct); ok && len(lt) > 0 && lt[0] == '{' { slt := e.llvmStructType(st) e.emitStructCompare(reg, b.Op, st, slt, lv, rv) return } if ar, ok := safeUnderlying(b.X.SSAType()).(*Array); ok && len(lt) > 0 && lt[0] == '[' { e.emitArrayCompare(reg, b.Op, ar, lt, lv, rv) return } u2 := safeUnderlying(b.X.SSAType()) _, isSig2 := u2.(*Signature) _, isIfce2 := u2.(*TCInterface) if !isSig2 && !isIfce2 && u2 == nil && lt == "{ptr, ptr}" { isIfce2 = true } if isSig2 || isIfce2 { nilField := "0" if isSig2 { nilField = "1" } rt2 := "ptr" if b.Y != nil && b.Y.SSAType() != nil { rt2 = e.llvmType(b.Y.SSAType()) } if lt == "{ptr, ptr}" && rt2 == "ptr" { e.nextReg++ extReg := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", ") ; e.w(nilField) ; e.w("\n") cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(extReg) ; e.w(", ") ; e.w(rv) ; e.w("\n") return } if lt == "ptr" && rt2 == "{ptr, ptr}" { e.nextReg++ extReg := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", ") ; e.w(nilField) ; e.w("\n") cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(lv) ; e.w(", ") ; e.w(extReg) ; e.w("\n") return } if lt == "{ptr, ptr}" && rt2 != "{ptr, ptr}" && rt2 != "ptr" { sty := e.sliceType() e.nextReg++ tmp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(rt2) ; e.w("\n") e.w(" store ") ; e.w(rt2) ; e.w(" ") ; e.w(rv) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.nextReg++ dp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(dp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 1\n") if rt2 == sty { e.nextReg++ ldp := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(ldp) ; e.w(" = load ") ; e.w(sty) ; e.w(", ptr ") ; e.w(dp) ; e.w("\n") e.nextReg++ lpA := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(lpA) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(ldp) ; e.w(", 0\n") e.nextReg++ lpB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(lpB) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 0\n") e.nextReg++ llA := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(llA) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(ldp) ; e.w(", 1\n") e.nextReg++ llB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(llB) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 1\n") e.nextReg++ cA := "%fc" | irItoa(e.nextReg) e.nextReg++ cB := "%fc" | irItoa(e.nextReg) cmpOp := "icmp eq" combOp := "and" if b.Op == OpNeq { cmpOp = "icmp ne" combOp = "or" } e.w(" ") ; e.w(cA) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(lpA) ; e.w(", ") ; e.w(lpB) ; e.w("\n") e.w(" ") ; e.w(cB) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(llA) ; e.w(", ") ; e.w(llB) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(combOp) ; e.w(" i1 ") ; e.w(cA) ; e.w(", ") ; e.w(cB) ; e.w("\n") return } cmpOp := "icmp eq" if b.Op == OpNeq { cmpOp = "icmp ne" } e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(dp) ; e.w(", ") ; e.w(tmp) ; e.w("\n") return } e.nextReg++ pA := "%fc" | irItoa(e.nextReg) e.nextReg++ pB := "%fc" | irItoa(e.nextReg) e.nextReg++ qA := "%fc" | irItoa(e.nextReg) e.nextReg++ qB := "%fc" | irItoa(e.nextReg) e.nextReg++ cA := "%fc" | irItoa(e.nextReg) e.nextReg++ cB := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(pA) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 0\n") e.w(" ") ; e.w(pB) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", 0\n") e.w(" ") ; e.w(qA) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(lv) ; e.w(", 1\n") e.w(" ") ; e.w(qB) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(rv) ; e.w(", 1\n") cmpOp := "icmp eq" combOp := "and" if b.Op == OpNeq { cmpOp = "icmp ne" combOp = "or" } e.w(" ") ; e.w(cA) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(pA) ; e.w(", ") ; e.w(pB) ; e.w("\n") e.w(" ") ; e.w(cB) ; e.w(" = ") ; e.w(cmpOp) ; e.w(" ptr ") ; e.w(qA) ; e.w(", ") ; e.w(qB) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = ") ; e.w(combOp) ; e.w(" i1 ") ; e.w(cA) ; e.w(", ") ; e.w(cB) ; e.w("\n") return } } if b.Op == OpAndNot { rt := "" if b.Y != nil && b.Y.SSAType() != nil { rt = e.llvmType(b.Y.SSAType()) } if rt != "" && rt != lt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { rv = e.coerceInt(rv, rt, lt) } e.nextReg++ notReg := "%an" | irItoa(e.nextReg) allOnes := "-1" e.w(" ") ; e.w(notReg) ; e.w(" = xor ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(allOnes) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = and ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(notReg) ; e.w("\n") return } if b.Y == nil || b.Y.SSAType() == nil { e.emitZeroReg(e.regName(b), b.SSAType()) return } rt := e.llvmType(b.Y.SSAType()) atl0 := e.resolvedType(b.X, lt) if atl0 != lt { lt = atl0 } atr0 := e.resolvedType(b.Y, rt) if atr0 != rt { rt = atr0 } if lt != rt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { isCmp := b.Op == OpEql || b.Op == OpNeq || b.Op == OpLss || b.Op == OpGtr || b.Op == OpLeq || b.Op == OpGeq resType := e.llvmType(b.SSAType()) if !isCmp && e.intBits(resType) > 0 { if lt != resType { lv = e.coerceInt(lv, lt, resType) lt = resType } if rt != resType { rv = e.coerceInt(rv, rt, resType) } } else if e.intBits(lt) > e.intBits(rt) { rv = e.coerceInt(rv, rt, lt) } else { lv = e.coerceInt(lv, lt, rt) lt = rt } } atl := e.resolvedType(b.X, lt) if atl != lt { lt = atl } if b.Y != nil { atr := e.resolvedType(b.Y, rt) if atr != rt { rt = atr } } resultIsInt := e.intBits(e.llvmType(b.SSAType())) > 0 isLF := lt == "double" || lt == "float" isRF := rt == "double" || rt == "float" if !isRF && isConstOperand(rv) && looksLikeFloat(rv) { if resultIsInt { if iv, ok := floatLitToInt(rv); ok { rv = irItoa64(iv) } } else { isRF = true rt = "double" } } if !isLF && isConstOperand(lv) && looksLikeFloat(lv) { if resultIsInt { if iv, ok := floatLitToInt(lv); ok { lv = irItoa64(iv) } } else { isLF = true lt = "double" } } if isLF && isRF { ssaLT := e.llvmType(b.X.SSAType()) ssaRT := "" if b.Y != nil && b.Y.SSAType() != nil { ssaRT = e.llvmType(b.Y.SSAType()) } if !isConstOperand(lv) && e.intBits(ssaLT) > 0 { _, hasRT := e.regTypes[lv] if !hasRT { lv = e.intToFloat(lv, ssaLT, lt) } } if !isConstOperand(rv) && e.intBits(ssaRT) > 0 { _, hasRT := e.regTypes[rv] if !hasRT { rv = e.intToFloat(rv, ssaRT, rt) } } } else if !isLF && isRF && e.intBits(lt) > 0 { if resultIsInt { demoted := false if yc, ok := b.Y.(*SSAConst); ok { if cf, ok2 := yc.val.(constFloat); ok2 { lit := cf.lit if lit == "" { lit = cf.String() } if iv, ok3 := floatLitToInt(lit); ok3 { rv = irItoa64(iv) rt = lt isRF = false demoted = true } } } if !demoted { if isConstOperand(lv) { lv = ensureFloatLit(lv) } else { lv = e.intToFloat(lv, lt, rt) } lt = rt } } else { if isConstOperand(lv) { lv = ensureFloatLit(lv) } else { lv = e.intToFloat(lv, lt, rt) } lt = rt } } else if isLF && !isRF && e.intBits(rt) > 0 { if resultIsInt { if xc, ok := b.X.(*SSAConst); ok { if cf, ok2 := xc.val.(constFloat); ok2 { lit := cf.lit if lit == "" { lit = cf.String() } if iv, ok3 := floatLitToInt(lit); ok3 { lv = irItoa64(iv) lt = rt isLF = false } } } if isLF { if isConstOperand(rv) { rv = ensureFloatLit(rv) } else { rv = e.intToFloat(rv, rt, lt) } rt = lt } } else { if isConstOperand(rv) { rv = ensureFloatLit(rv) } else { rv = e.intToFloat(rv, rt, lt) } rt = lt } } if lt == "float" && rt == "double" { e.nextReg++ tmp := "%fext" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = fpext float ") ; e.w(lv) ; e.w(" to double\n") lv = tmp lt = "double" } else if lt == "double" && rt == "float" { e.nextReg++ tmp := "%fext" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = fpext float ") ; e.w(rv) ; e.w(" to double\n") rv = tmp rt = "double" } op := e.llvmBinOp(b.Op, b.X.SSAType()) if op == "" { e.w(" ; unsupported binop\n") return } isCmpOp := b.Op == OpEql || b.Op == OpNeq || b.Op == OpLss || b.Op == OpGtr || b.Op == OpLeq || b.Op == OpGeq if lt == "double" || lt == "float" { rv = ensureFloatLit(rv) lv = ensureFloatLit(lv) op = e.floatBinOp(b.Op) if !isCmpOp { e.setRegType(b, reg, lt) } } else if !isCmpOp { ssaLT := e.llvmType(b.X.SSAType()) if ssaLT != lt { e.setRegType(b, reg, lt) } } if len(lt) > 0 && lt[0] == '[' && (b.Op == OpEql || b.Op == OpNeq) { e.emitArrayCompareByLLVM(reg, b.Op, lt, lv, rv) return } if lt == "ptr" && !isCmpOp { e.nextReg++ pi := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(pi) ; e.w(" = ptrtoint ptr ") ; e.w(lv) ; e.w(" to ") ; e.w(e.intptrType()) ; e.w("\n") e.nextReg++ ri := "%pi" | irItoa(e.nextReg) rvCoerced := rv if rv != "0" && rv != "1" && rv != "-1" { e.w(" ") ; e.w(ri) ; e.w(" = ptrtoint ptr ") ; e.w(rv) ; e.w(" to ") ; e.w(e.intptrType()) ; e.w("\n") rvCoerced = ri } e.nextReg++ ai := "%pi" | irItoa(e.nextReg) e.w(" ") ; e.w(ai) ; e.w(" = ") ; e.w(op) ; e.w(" ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(pi) ; e.w(", ") ; e.w(rvCoerced) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = inttoptr ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(ai) ; e.w(" to ptr\n") return } e.w(" ") e.w(reg) e.w(" = ") e.w(op) e.w(" ") e.w(lt) e.w(" ") e.w(lv) e.w(", ") e.w(rv) e.w("\n") } func looksLikeFloat(s string) bool { if len(s) == 0 { return false } if s[0] != '-' && s[0] != '+' && (s[0] < '0' || s[0] > '9') { return false } for i := 0; i < len(s); i++ { if s[i] == '.' || s[i] == 'e' || s[i] == 'E' { return true } } return false } func floatLitToInt(s string) (int64, bool) { if len(s) == 0 { return 0, false } i := 0 neg := false if s[0] == '-' { neg = true i = 1 } else if s[0] == '+' { i = 1 } var intPart int64 for ; i < len(s); i++ { ch := s[i] if ch == '_' { continue } if ch < '0' || ch > '9' { break } intPart = intPart*10 + int64(ch-'0') } var fracDigits int if i < len(s) && s[i] == '.' { i++ for ; i < len(s); i++ { ch := s[i] if ch == '_' { continue } if ch < '0' || ch > '9' { break } if ch != '0' { return 0, false } fracDigits++ } } _ = fracDigits exp := 0 if i < len(s) && (s[i] == 'e' || s[i] == 'E') { i++ expNeg := false if i < len(s) && s[i] == '-' { expNeg = true i++ } else if i < len(s) && s[i] == '+' { i++ } for ; i < len(s); i++ { ch := s[i] if ch < '0' || ch > '9' { break } exp = exp*10 + int(ch-'0') } if expNeg { return 0, false } } result := intPart for j := 0; j < exp; j++ { result = result * 10 if result < 0 { return 0, false } } if neg { result = -result } if result == 0 { return 0, false } return result, true } func isConstOperand(s string) bool { if len(s) == 0 { return false } return s[0] != '%' && s[0] != '@' } func ensureFloatLit(s string) string { if len(s) == 0 || s[0] == '%' || s[0] == '@' { return s } hasDecimal := false for i := 0; i < len(s); i++ { if s[i] == '.' || s[i] == 'e' || s[i] == 'E' { hasDecimal = true break } } if !hasDecimal { return s | ".0" } return s } func (e *irEmitter) emitSliceConcat(reg string, sl *Slice, lv, rv string) { ipt := e.intptrType() sty := "{ptr, " | ipt | ", " | ipt | "}" if isBareLiteral(lv) { lv = "zeroinitializer" } if isBareLiteral(rv) { rv = "zeroinitializer" } elemType := e.llvmType(sl.Elem()) xPtr := e.nextReg2("cc") e.w(" ") ; e.w(xPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 0\n") xLen := e.nextReg2("cc") e.w(" ") ; e.w(xLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 1\n") yPtr := e.nextReg2("cc") e.w(" ") ; e.w(yPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 0\n") yLen := e.nextReg2("cc") e.w(" ") ; e.w(yLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 1\n") elemSz := e.nextReg2("cc") e.w(" ") ; e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") retTy := "{ptr, " | ipt | ", " | ipt | "}" result := e.nextReg2("cc") e.w(" ") ; e.w(result) e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ") e.w(xPtr) ; e.w(", ptr ") ; e.w(yPtr) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(xLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(xLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(yLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := e.nextReg2("cc") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := e.nextReg2("cc") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := e.nextReg2("cc") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := e.nextReg2("cc") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := e.nextReg2("cc") e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n") e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | ipt | ", " | ipt | ", " | ipt | ", " | ipt) } func isBareLiteral(s string) bool { if len(s) == 0 { return false } return s[0] >= '0' && s[0] <= '9' } func (e *irEmitter) emitStringCompare(reg string, op SSAOp, lv, rv string) { ipt := e.intptrType() sty := "{ptr, " | ipt | ", " | ipt | "}" if lv == "null" { lv = "zeroinitializer" } if rv == "null" { rv = "zeroinitializer" } if isBareLiteral(lv) { lv = "zeroinitializer" } if isBareLiteral(rv) { rv = "zeroinitializer" } switch op { case OpEql: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringEqual(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) case OpNeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringEqual(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) case OpLss: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case OpGtr: e.w(" ") ; e.w(reg) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(")\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case OpLeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) case OpGeq: tmp := e.nextReg2("sc") e.w(" ") ; e.w(tmp) ; e.w(" = call i1 @runtime.stringLess(") e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(")\n") e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(tmp) ; e.w(", -1\n") e.declareRuntime("runtime.stringLess", "i1", sty | ", " | sty) default: e.w(" ; unsupported string binop\n") } } func (e *irEmitter) emitStructCompare(reg string, op SSAOp, st *TCStruct, lt, lv, rv string) { n := st.NumFields() if n == 0 { if op == OpEql { e.valName[nil] = "true" e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = icmp ne i32 0, 0\n") } return } var lastCmp string for i := 0; i < n; i++ { ft := e.llvmType(st.Field(i).Type()) lf := e.nextReg2("sf") rf := e.nextReg2("sf") e.w(" ") ; e.w(lf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" ") ; e.w(rf) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") cmp := e.nextReg2("sf") if e.isStringLike(st.Field(i).Type()) { sty := e.sliceType() e.w(" ") ; e.w(cmp) ; e.w(" = call i1 @runtime.stringEqual(") ; e.w(sty) ; e.w(" ") ; e.w(lf) ; e.w(", ") ; e.w(sty) ; e.w(" ") ; e.w(rf) ; e.w(")\n") e.declareRuntime("runtime.stringEqual", "i1", sty | ", " | sty) } else { e.w(" ") ; e.w(cmp) ; e.w(" = icmp eq ") ; e.w(ft) ; e.w(" ") ; e.w(lf) ; e.w(", ") ; e.w(rf) ; e.w("\n") } if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("sf") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else if n == 1 { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func (e *irEmitter) emitArrayCompare(reg string, op SSAOp, ar *Array, lt, lv, rv string) { n := int(ar.Len()) if n == 0 { if op == OpEql { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = icmp ne i32 0, 0\n") } return } et := e.llvmType(ar.Elem()) isZeroL := lv == "0" || lv == "zeroinitializer" isZeroR := rv == "0" || rv == "zeroinitializer" var lastCmp string for i := 0; i < n; i++ { var lfr, rfr string if isZeroL { lfr = "0" } else { lfr = e.nextReg2("ae") e.w(" ") ; e.w(lfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } if isZeroR { rfr = "0" } else { rfr = e.nextReg2("ae") e.w(" ") ; e.w(rfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } cmp := e.nextReg2("ae") e.w(" ") ; e.w(cmp) ; e.w(" = icmp eq ") ; e.w(et) ; e.w(" ") ; e.w(lfr) ; e.w(", ") ; e.w(rfr) ; e.w("\n") if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("ae") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func parseArrayType(lt string) (int, string) { if len(lt) < 5 || lt[0] != '[' { return 0, "" } i := 1 for i < len(lt) && lt[i] >= '0' && lt[i] <= '9' { i++ } n := 0 for j := 1; j < i; j++ { n = n*10 + int(lt[j]-'0') } if i+3 >= len(lt) || lt[i] != ' ' || lt[i+1] != 'x' || lt[i+2] != ' ' { return 0, "" } et := lt[i+3 : len(lt)-1] return n, et } func (e *irEmitter) emitArrayCompareByLLVM(reg string, op SSAOp, lt, lv, rv string) { n, et := parseArrayType(lt) if n == 0 { e.w(" ") ; e.w(reg) ; e.w(" = icmp eq i32 0, 0\n") return } isZeroL := lv == "0" || lv == "zeroinitializer" isZeroR := rv == "0" || rv == "zeroinitializer" var lastCmp string for i := 0; i < n; i++ { var lfr, rfr string if isZeroL { lfr = "0" } else { lfr = e.nextReg2("ae") e.w(" ") ; e.w(lfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(lv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } if isZeroR { rfr = "0" } else { rfr = e.nextReg2("ae") e.w(" ") ; e.w(rfr) ; e.w(" = extractvalue ") ; e.w(lt) ; e.w(" ") ; e.w(rv) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") } cmp := e.nextReg2("ae") e.w(" ") ; e.w(cmp) ; e.w(" = icmp eq ") ; e.w(et) ; e.w(" ") ; e.w(lfr) ; e.w(", ") ; e.w(rfr) ; e.w("\n") if i == 0 { lastCmp = cmp } else { acc := e.nextReg2("ae") e.w(" ") ; e.w(acc) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", ") ; e.w(cmp) ; e.w("\n") lastCmp = acc } } if op == OpNeq { e.w(" ") ; e.w(reg) ; e.w(" = xor i1 ") ; e.w(lastCmp) ; e.w(", -1\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = and i1 ") ; e.w(lastCmp) ; e.w(", true\n") } } func (e *irEmitter) llvmBinOp(op SSAOp, typ Type) string { isFloat := false isSigned := true if typ != nil { if b, ok := safeUnderlying(typ).(*Basic); ok { if b.Info()&IsFloat != 0 { isFloat = true } if b.Info()&IsUnsigned != 0 { isSigned = false } } } if isFloat { switch op { case OpAdd: return "fadd" case OpSub: return "fsub" case OpMul: return "fmul" case OpQuo: return "fdiv" case OpEql: return "fcmp oeq" case OpNeq: return "fcmp une" case OpLss: return "fcmp olt" case OpLeq: return "fcmp ole" case OpGtr: return "fcmp ogt" case OpGeq: return "fcmp oge" } return "" } switch op { case OpAdd: return "add" case OpSub: return "sub" case OpMul: return "mul" case OpQuo: if isSigned { return "sdiv" } return "udiv" case OpRem: if isSigned { return "srem" } return "urem" case OpAnd, OpLand: return "and" case OpOr, OpLor: return "or" case OpXor: return "xor" case OpShl: return "shl" case OpShr: if isSigned { return "ashr" } return "lshr" case OpAndNot: return "" case OpEql: return "icmp eq" case OpNeq: return "icmp ne" case OpLss: if isSigned { return "icmp slt" } return "icmp ult" case OpLeq: if isSigned { return "icmp sle" } return "icmp ule" case OpGtr: if isSigned { return "icmp sgt" } return "icmp ugt" case OpGeq: if isSigned { return "icmp sge" } return "icmp uge" } return "" } func (e *irEmitter) emitUnOp(u *SSAUnOp) { reg := e.regName(u) if u.Op == OpMul { loadType := e.llvmType(u.SSAType()) if loadType == "void" { if at, ok := e.allocTypes[u.X]; ok { loadType = at } else if a, ok := u.X.(*SSAAlloc); ok { loadType = e.inferAllocTypeFromStores(a) } else { loadType = "ptr" } e.allocTypes[u] = loadType } if g, ok := u.X.(*SSAGlobal); ok { loadType = e.resolveGlobalDeclType(g) } if at, ok := e.allocTypes[u.X]; ok && at != "ptr" && at != "void" && at != loadType { bothScalar := len(loadType) > 0 && loadType[0] == 'i' && len(at) > 0 && at[0] == 'i' isArrayElem := len(at) > 0 && at[0] == '[' && len(loadType) > 0 && loadType[0] != '{' && loadType != at bothAgg := len(loadType) > 0 && loadType[0] == '{' && len(at) > 0 && at[0] == '{' if !bothScalar && !isArrayElem && !bothAgg { loadType = at e.allocTypes[u] = loadType } } addr := e.operand(u.X) e.w(" ") e.w(reg) e.w(" = load ") e.w(loadType) e.w(", ptr ") e.w(addr) e.w("\n") if loadType == "double" || loadType == "float" { e.setRegType(u, reg, loadType) } return } valType := e.llvmType(u.X.SSAType()) resolved := e.resolvedType(u.X, valType) if resolved != valType { valType = resolved } val := e.operand(u.X) if u.Op == OpSub { isFloat := false if b, ok := safeUnderlying(u.X.SSAType()).(*Basic); ok { isFloat = b.Info()&IsFloat != 0 } if !isFloat && (valType == "double" || valType == "float") { isFloat = true } if !isFloat && isConstOperand(val) && looksLikeFloat(val) { isFloat = true valType = "double" } e.w(" ") e.w(reg) if isFloat { e.w(" = fneg ") e.w(valType) e.w(" ") e.w(val) e.w("\n") e.setRegType(u, reg, valType) } else { e.w(" = sub ") e.w(valType) e.w(" 0, ") e.w(val) e.w("\n") } return } if u.Op == OpNot || u.Op == OpXor { e.w(" ") e.w(reg) e.w(" = xor ") e.w(valType) e.w(" ") e.w(val) e.w(", -1\n") return } e.w(" ; unsupported unop op=") e.w(u.Op.String()) e.w("\n") } func (e *irEmitter) callArgType(arg SSAValue, sig *Signature, i int) string { if _, isFreeVar := arg.(*SSAFreeVar); isFreeVar { return "ptr" } t := e.llvmType(arg.SSAType()) if _, isAlloc := arg.(*SSAAlloc); !isAlloc { resolved := e.resolvedType(arg, t) if resolved != t { t = resolved } } if (t == "ptr" || t == "i1") && sig != nil && sig.Params() != nil { _, isCall := arg.(*SSACall) _, isAlloc := arg.(*SSAAlloc) if !isCall && !isAlloc { sigIdx := i if sig.Recv() != nil { sigIdx = i - 1 } if sigIdx >= 0 && sigIdx < sig.Params().Len() { sigT := e.llvmType(sig.Params().At(sigIdx).Type()) if sigT != "ptr" && sigT != "void" && sigT != "i1" && sigT != "" { return sigT } } } } if t == "ptr" || t == "i1" { if load, ok := arg.(*SSAUnOp); ok && load.Op == OpMul { if g, ok2 := load.X.(*SSAGlobal); ok2 { if gt, ok3 := e.globalTypes[e.globalName(g)]; ok3 { return gt } } } } if t != "void" { return t } if sig != nil && sig.Params() != nil { sigIdx := i if sig.Recv() != nil { sigIdx = i - 1 } if sigIdx >= 0 && sigIdx < sig.Params().Len() { return e.llvmType(sig.Params().At(sigIdx).Type()) } } return "ptr" } func (e *irEmitter) callSig(c *SSACall) *Signature { if fn, ok := c.Call.Value.(*SSAFunction); ok && fn.Signature != nil { return fn.Signature } if sig, ok := safeUnderlying(c.Call.Value.SSAType()).(*Signature); ok { return sig } return nil } func (e *irEmitter) emitCall(c *SSACall) { if b, ok := c.Call.Value.(*SSABuiltin); ok { e.emitBuiltinCall(c, b) return } reg := e.regName(c) retType := e.llvmType(c.SSAType()) isVoid := retType == "void" sig := e.callSig(c) if fn, ok := c.Call.Value.(*SSAFunction); ok { if !e.isPkgFunc(fn) { e.declareExternalFunc(fn) } e.w(" ") if !isVoid { e.w(reg) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") e.w(e.funcSymbol(fn)) e.w("(") for i, arg := range c.Call.Args { if i > 0 { e.w(", ") } if arg == nil { e.w("ptr null") continue } at := e.callArgType(arg, sig, i) av := e.operand(arg) if av == "null" && at != "ptr" { av = "zeroinitializer" } e.w(at) ; e.w(" ") ; e.w(av) } if !fn.isExternC { if len(c.Call.Args) > 0 { e.w(", ") } e.w("ptr null") } e.w(")\n") return } funcVal := e.operand(c.Call.Value) funcPtr := e.nextReg2("fp") ctx := e.nextReg2("ctx") e.w(" ") ; e.w(funcPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 1\n") e.w(" ") ; e.w(ctx) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(funcVal) ; e.w(", 0\n") e.w(" ") if !isVoid { e.w(reg) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(funcPtr) ; e.w("(") for i, arg := range c.Call.Args { if i > 0 { e.w(", ") } at := e.callArgType(arg, sig, i) av := e.operand(arg) if av == "null" && at != "ptr" { av = "zeroinitializer" } e.w(at) ; e.w(" ") ; e.w(av) } if len(c.Call.Args) > 0 { e.w(", ") } e.w("ptr ") ; e.w(ctx) e.w(")\n") } func (e *irEmitter) emitBuiltinCall(c *SSACall, b *SSABuiltin) { reg := e.regName(c) name := b.SSAName() if name == "recover" { e.w(" ; unhandled builtin: recover\n") retType := e.ifaceType() e.nextReg++ tmp := "%ub" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.allocTypes[c] = retType return } ipt := e.intptrType() sty := e.sliceType() if name == "len" { if len(c.Call.Args) == 1 { arg := e.operand(c.Call.Args[0]) u := safeUnderlying(c.Call.Args[0].SSAType()) if u == nil { u = c.Call.Args[0].SSAType() } if arr, ok := u.(*Array); ok { retType := e.llvmType(c.SSAType()) e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(retType) ; e.w(" ") ; e.w(irItoa(int(arr.Len()))) ; e.w(", 0\n") _ = arg return } if p, ok := u.(*Pointer); ok && p.Elem() != nil { if arr, ok2 := safeUnderlying(p.Elem()).(*Array); ok2 { retType := e.llvmType(c.SSAType()) e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(retType) ; e.w(" ") ; e.w(irItoa(int(arr.Len()))) ; e.w(", 0\n") _ = arg return } } _, isSlice := u.(*Slice) _, isMap := u.(*TCMap) isStr := e.isStringLike(c.Call.Args[0].SSAType()) if !isSlice && !isMap && !isStr { isSlice = true } if isMap { retType := e.llvmType(c.SSAType()) e.nextReg++ tmp := "%bl" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = call ") ; e.w(ipt) ; e.w(" @runtime.hashmapLen(ptr ") ; e.w(arg) ; e.w(")\n") if retType != ipt { e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(retType) ; e.w("\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(", 0\n") } e.declareRuntime("runtime.hashmapLen", ipt, "ptr") return } if isSlice || isStr { retType := e.llvmType(c.SSAType()) if retType != ipt { e.nextReg++ tmp := "%bl" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(retType) ; e.w("\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 1\n") } return } } } else if name == "cap" { if len(c.Call.Args) == 1 { arg := e.operand(c.Call.Args[0]) uc := safeUnderlying(c.Call.Args[0].SSAType()) if uc == nil { uc = c.Call.Args[0].SSAType() } if arr, ok := uc.(*Array); ok { retType := e.llvmType(c.SSAType()) e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(retType) ; e.w(" ") ; e.w(irItoa(int(arr.Len()))) ; e.w(", 0\n") _ = arg return } if p, ok := uc.(*Pointer); ok && p.Elem() != nil { if arr, ok2 := safeUnderlying(p.Elem()).(*Array); ok2 { retType := e.llvmType(c.SSAType()) e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(retType) ; e.w(" ") ; e.w(irItoa(int(arr.Len()))) ; e.w(", 0\n") _ = arg return } } _, isSlice := uc.(*Slice) isStr := e.isStringLike(c.Call.Args[0].SSAType()) if isSlice || isStr { retType := e.llvmType(c.SSAType()) if retType != ipt { e.nextReg++ tmp := "%bl" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n") e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(" to ") ; e.w(retType) ; e.w("\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(arg) ; e.w(", 2\n") } return } } } else if name == "append" { if len(c.Call.Args) > 2 { src := e.operand(c.Call.Args[0]) elemType := "" if sl, ok := safeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } if sl, ok := c.Call.Args[0].SSAType().(*Slice); ok && (elemType == "" || elemType == "void") { elemType = e.llvmType(sl.Elem()) } if elemType == "" || elemType == "void" { et := e.llvmType(c.Call.Args[1].SSAType()) if et != "" && et != "void" { elemType = et } else { elemType = "i8" } } nElems := len(c.Call.Args) - 1 arrAlloca := e.nextReg2("ap") arrTy := "[" | irItoa(nElems) | " x " | elemType | "]" e.w(" ") ; e.w(arrAlloca) ; e.w(" = alloca ") ; e.w(arrTy) ; e.w("\n") for j := 1; j < len(c.Call.Args); j++ { elemVal := e.operand(c.Call.Args[j]) argLLT := e.llvmType(c.Call.Args[j].SSAType()) if len(argLLT) > 1 && argLLT[0] == 'i' && len(elemType) > 1 && elemType[0] == 'i' && argLLT != elemType { aw := irParseIntWidth(argLLT) ew := irParseIntWidth(elemType) if ew > 0 && aw > ew { tr := e.nextReg2("ap") e.w(" ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(argLLT) ; e.w(" ") ; e.w(elemVal) ; e.w(" to ") ; e.w(elemType) ; e.w("\n") elemVal = tr } } gep := e.nextReg2("ap") e.w(" ") ; e.w(gep) ; e.w(" = getelementptr inbounds ") ; e.w(arrTy) e.w(", ptr ") ; e.w(arrAlloca) ; e.w(", i32 0, i32 ") ; e.w(irItoa(j-1)) ; e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" ") ; e.w(elemVal) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") } srcBuf := e.nextReg2("ap") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := e.nextReg2("ap") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") srcCap := e.nextReg2("ap") e.w(" ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n") elemSz := e.nextReg2("ap") e.w(" ") ; e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n") retTy := e.sliceType() result := e.nextReg2("ap") e.w(" ") ; e.w(result) e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ") e.w(srcBuf) ; e.w(", ptr ") ; e.w(arrAlloca) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(irItoa(nElems)) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := e.nextReg2("ap") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := e.nextReg2("ap") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := e.nextReg2("ap") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := e.nextReg2("ap") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := e.nextReg2("ap") e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n") e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType()) return } if len(c.Call.Args) == 2 { src := e.operand(c.Call.Args[0]) elems := e.operand(c.Call.Args[1]) elemType := "" arg0t := c.Call.Args[0].SSAType() if sl, ok := safeUnderlying(arg0t).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } if sl, ok := arg0t.(*Slice); ok && (elemType == "" || elemType == "void") { elemType = e.llvmType(sl.Elem()) } if elemType == "" || elemType == "void" { et := e.llvmType(c.Call.Args[1].SSAType()) if et != "" && et != "void" { elemType = et } else { elemType = "i8" } } srcBuf := e.nextReg2("ap") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := e.nextReg2("ap") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") srcCap := e.nextReg2("ap") e.w(" ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n") var elemsBuf, elemsLen string arg1IsSlice := c.Call.HasDots if !arg1IsSlice && c.Call.Args[1] != nil { arg1t := c.Call.Args[1].SSAType() if arg1t != nil { arg1LT := e.llvmType(arg1t) if arg1LT == e.sliceType() && elemType != e.sliceType() { arg1IsSlice = true } } if !arg1IsSlice { arg0t := c.Call.Args[0].SSAType() if arg0t != nil && arg1t != nil { if Identical(arg0t, arg1t) { if _, ok := safeUnderlying(arg0t).(*Slice); ok { arg1IsSlice = true } } } } } if arg1IsSlice { elemsBuf = e.nextReg2("ap") e.w(" ") ; e.w(elemsBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 0\n") elemsLen = e.nextReg2("ap") e.w(" ") ; e.w(elemsLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 1\n") } else { alloca := e.nextReg2("ap") e.w(" ") ; e.w(alloca) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") e.w(" store ") ; e.w(elemType) ; e.w(" ") ; e.w(elems) ; e.w(", ptr ") ; e.w(alloca) ; e.w("\n") elemsBuf = alloca elemsLen = "1" } elemSz := e.nextReg2("ap") e.w(" ") ; e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n") retTy := e.sliceType() result := e.nextReg2("ap") e.w(" ") ; e.w(result) e.w(" = call ") ; e.w(retTy) ; e.w(" @runtime.sliceAppend(ptr ") e.w(srcBuf) ; e.w(", ptr ") ; e.w(elemsBuf) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcCap) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemsLen) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := e.nextReg2("ap") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := e.nextReg2("ap") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := e.nextReg2("ap") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := e.nextReg2("ap") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := e.nextReg2("ap") e.w(" ") ; e.w(s2) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s1) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newLen) ; e.w(", 1\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" ") ; e.w(s2) ; e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(newCap) ; e.w(", 2\n") e.declareRuntime("runtime.sliceAppend", retTy, "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType()) return } } else if name == "copy" { if len(c.Call.Args) == 2 { dst := e.operand(c.Call.Args[0]) src := e.operand(c.Call.Args[1]) elemType := "i8" if sl, ok := safeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } dstBuf := e.nextReg2("cp") e.w(" ") ; e.w(dstBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 0\n") dstLen := e.nextReg2("cp") e.w(" ") ; e.w(dstLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 1\n") srcBuf := e.nextReg2("cp") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := e.nextReg2("cp") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") elemSz := e.nextReg2("cp") e.w(" ") ; e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(e.intptrType()) ; e.w("\n") callReg := e.nextReg2("cp") e.w(" ") ; e.w(callReg) e.w(" = call ") ; e.w(e.intptrType()) ; e.w(" @runtime.sliceCopy(ptr ") e.w(dstBuf) ; e.w(", ptr ") ; e.w(srcBuf) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(dstLen) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(elemSz) e.w(")\n") retType := e.llvmType(c.SSAType()) if retType != e.intptrType() { e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(" to ") ; e.w(retType) ; e.w("\n") } else { e.w(" ") ; e.w(reg) ; e.w(" = add ") ; e.w(e.intptrType()) ; e.w(" ") ; e.w(callReg) ; e.w(", 0\n") } e.declareRuntime("runtime.sliceCopy", e.intptrType(), "ptr, ptr, " | e.intptrType() | ", " | e.intptrType() | ", " | e.intptrType()) return } } else if name == "print" || name == "println" { e.w(" call void @runtime.printlock()\n") for i, arg := range c.Call.Args { if i > 0 && b.id == BuiltinPrintln { e.w(" call void @runtime.printspace()\n") } av := e.operand(arg) at := arg.SSAType() e.emitPrintArg(av, at) } if b.id == BuiltinPrintln { e.w(" call void @runtime.printnl()\n") e.declareRuntime("runtime.printnl", "void", "") } e.w(" call void @runtime.printunlock()\n") e.declareRuntime("runtime.printlock", "void", "") e.declareRuntime("runtime.printunlock", "void", "") if b.id == BuiltinPrintln && len(c.Call.Args) > 1 { e.declareRuntime("runtime.printspace", "void", "") } return } else if name == "delete" { if len(c.Call.Args) == 2 { mapVal := e.operand(c.Call.Args[0]) keyVal := e.operand(c.Call.Args[1]) var mt *TCMap if okv, okok := safeUnderlying(c.Call.Args[0].SSAType()).(*TCMap); okok { mt = okv } keyType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) } keyAlloca := e.nextReg2("dl") 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") e.w(" call void @runtime.hashmapBinaryDelete(ptr ") ; e.w(mapVal) e.w(", ptr ") ; e.w(keyAlloca) ; e.w(")\n") e.declareRuntime("runtime.hashmapBinaryDelete", "void", "ptr, ptr") return } } else if name == "close" { if len(c.Call.Args) == 1 { e.w(" call void @runtime.chanClose(ptr ") e.w(e.operand(c.Call.Args[0])) e.w(")\n") e.declareRuntime("runtime.chanClose", "void", "ptr") return } } else if name == "min" || name == "max" { if len(c.Call.Args) >= 2 { retType := e.llvmType(c.SSAType()) if retType == "" || retType == "void" { retType = "i32" } a := e.operand(c.Call.Args[0]) b2 := e.operand(c.Call.Args[1]) cmpOp := "slt" if b.id == BuiltinMax { cmpOp = "sgt" } u := safeUnderlying(c.SSAType()) if bb, ok := u.(*Basic); ok && bb.Info()&IsUnsigned != 0 { cmpOp = "ult" if b.id == BuiltinMax { cmpOp = "ugt" } } e.nextReg++ cmpReg := "%mm" | irItoa(e.nextReg) e.w(" ") ; e.w(cmpReg) ; e.w(" = icmp ") ; e.w(cmpOp) ; e.w(" ") ; e.w(retType) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(b2) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = select i1 ") ; e.w(cmpReg) ; e.w(", ") ; e.w(retType) ; e.w(" ") ; e.w(a) ; e.w(", ") ; e.w(retType) ; e.w(" ") ; e.w(b2) ; e.w("\n") return } } e.w(" ; unhandled builtin: ") e.w(name) e.w("\n") retType := e.llvmType(c.SSAType()) if retType != "void" && retType != "" { if retType == "ptr" || e.intBits(retType) > 0 || retType == "i1" { e.emitZeroReg(reg, c.SSAType()) } else { e.nextReg++ tmp := "%ub" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.allocTypes[c] = retType } } } func (e *irEmitter) emitPrintArg(val string, t Type) { if t == nil { return } sty := e.sliceType() switch u := safeUnderlying(t).(type) { case *Basic: switch { case u.Info()&IsString != 0: e.w(" call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printstring", "void", sty) case u.Kind() == Bool || u.Kind() == UntypedBool: e.w(" call void @runtime.printbool(i1 ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printbool", "void", "i1") case u.Kind() == Float32: e.w(" call void @runtime.printfloat32(float ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printfloat32", "void", "float") case u.Kind() == Float64 || u.Kind() == UntypedFloat: e.w(" call void @runtime.printfloat64(double ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printfloat64", "void", "double") case u.Info()&IsUnsigned != 0: lt := e.llvmType(t) fname := "runtime.printuint" | lt[1:] e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(")\n") e.declareRuntime(fname, "void", lt) case u.Info()&IsInteger != 0: lt := e.llvmType(t) fname := "runtime.printint" | lt[1:] e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(lt) ; e.w(" ") ; e.w(val) ; e.w(")\n") e.declareRuntime(fname, "void", lt) } case *Pointer: ipt := e.intptrType() e.nextReg++ tmp := "%pr" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") e.w(" call void @runtime.printptr(") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(")\n") e.declareRuntime("runtime.printptr", "void", ipt) case *Slice: if b, ok := u.Elem().(*Basic); ok && (b.Kind() == Uint8 || b.Kind() == Int8) { e.w(" call void @runtime.printbytes(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printbytes", "void", sty) } else { e.w(" call void @runtime.printstring(") ; e.w(sty) ; e.w(" ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printstring", "void", sty) } case *TCMap: e.w(" call void @runtime.printmap(ptr ") ; e.w(val) ; e.w(")\n") e.declareRuntime("runtime.printmap", "void", "ptr") } } func (e *irEmitter) emitPhi(p *SSAPhi) { reg := e.regName(p) typ := e.llvmType(p.SSAType()) e.w(" ") e.w(reg) e.w(" = phi ") e.w(typ) e.w(" ") blk := p.InstrBlock() if blk == nil { return } for i, edge := range p.Edges { if i > 0 { e.w(", ") } e.w("[") e.w(e.operand(edge)) e.w(", ") if blk != nil && i < len(blk.Preds) { pred := blk.Preds[i] if pred != nil { if exitLbl, ok := e.blockExitLabel[pred.Index]; ok { e.w(exitLbl) } else { e.w(e.blockLabel(pred)) } } else { e.w("%unknown") } } else { e.w("%unknown") } e.w("]") } e.w("\n") } func isNumericLiteral(s string) bool { if len(s) == 0 { return false } c := s[0] if c == '-' && len(s) > 1 { c = s[1] } return c >= '0' && c <= '9' } func (e *irEmitter) coerceInt(valReg string, fromType string, toType string) string { if fromType == toType { return valReg } fromBits := e.intBits(fromType) toBits := e.intBits(toType) if fromBits == 0 || toBits == 0 { return valReg } if isNumericLiteral(valReg) { return valReg } e.nextReg++ r := "%rc" | irItoa(e.nextReg) if fromBits > toBits { e.w(" ") ; e.w(r) ; e.w(" = trunc ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") } else { e.w(" ") ; e.w(r) ; e.w(" = sext ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") } return r } func (e *irEmitter) intBits(ty string) int32 { switch ty { case "i1": return 1 case "i8": return 8 case "i16": return 16 case "i32": return 32 case "i64": return 64 } return 0 } func (e *irEmitter) intToFloat(valReg string, fromType string, toType string) string { if rt, ok := e.regTypes[valReg]; ok && (rt == "double" || rt == "float") { return valReg } e.nextReg++ r := "%itf" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = sitofp ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") return r } func (e *irEmitter) floatBinOp(op SSAOp) string { switch op { case OpAdd: return "fadd" case OpSub: return "fsub" case OpMul: return "fmul" case OpQuo: return "fdiv" case OpEql: return "fcmp oeq" case OpNeq: return "fcmp une" case OpLss: return "fcmp olt" case OpGtr: return "fcmp ogt" case OpLeq: return "fcmp ole" case OpGeq: return "fcmp oge" } return "fadd" } func (e *irEmitter) arrayElemType(arrType string) string { // "[6 x double]" -> "double" xPos := -1 for i := 0; i < len(arrType); i++ { if arrType[i] == 'x' && i > 0 && arrType[i-1] == ' ' { xPos = i break } } if xPos < 0 || xPos+2 >= len(arrType) { return arrType } end := len(arrType) if arrType[end-1] == ']' { end = end - 1 } return arrType[xPos+2 : end] } func (e *irEmitter) emitReturn(r *SSAReturn) { frt := e.funcRetType(e.curFunc) if len(r.Results) == 0 { rt := e.funcRetType(e.curFunc) if rt == "void" { e.w(" ret void\n") } else if len(e.curFunc.NamedResults) > 0 { if len(e.curFunc.NamedResults) == 1 { nr := e.curFunc.NamedResults[0] nrt := e.llvmType(nr.SSAType()) e.nextReg++ tmp := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n") e.w(" ret ") ; e.w(nrt) ; e.w(" ") ; e.w(tmp) ; e.w("\n") } else { retType := rt e.nextReg++ agg := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(agg) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n") e.w(" store ") ; e.w(retType) ; e.w(" zeroinitializer, ptr ") ; e.w(agg) ; e.w("\n") for i, nr := range e.curFunc.NamedResults { nrt := e.llvmType(nr.SSAType()) e.nextReg++ tmp := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n") e.nextReg++ gep := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(retType) ; e.w(", ptr ") ; e.w(agg) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" store ") ; e.w(nrt) ; e.w(" ") ; e.w(tmp) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") } e.nextReg++ rv := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(rv) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(agg) ; e.w("\n") e.w(" ret ") ; e.w(retType) ; e.w(" ") ; e.w(rv) ; e.w("\n") } } else { e.w(" ret ") ; e.w(rt) ; e.w(" zeroinitializer\n") } return } sig := e.curFunc.Signature if len(r.Results) == 1 { typ := e.llvmType(r.Results[0].SSAType()) val := e.operand(r.Results[0]) expectType := typ if sig != nil && sig.Results() != nil && sig.Results().Len() == 1 { expectType = e.llvmType(sig.Results().At(0).Type()) } if typ == "void" { typ = frt } if expectType == "void" { expectType = frt } if val == "null" && expectType != "ptr" { val = "zeroinitializer" } else { val = e.coerceInt(val, typ, expectType) } if typ != expectType && val != "zeroinitializer" { if expectType == "ptr" && e.intBits(typ) > 0 { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = inttoptr ") ; e.w(typ) ; e.w(" ") ; e.w(val) ; e.w(" to ptr\n") val = rc typ = "ptr" } else if typ == "ptr" && e.intBits(expectType) > 0 { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(expectType) ; e.w("\n") val = rc typ = expectType } if typ != expectType { val = "zeroinitializer" } } e.w(" ret ") e.w(expectType) e.w(" ") e.w(val) e.w("\n") return } var expectTypes []string if sig != nil && sig.Results() != nil { for i := 0; i < sig.Results().Len(); i++ { expectTypes = append(expectTypes, e.resolveResultType(sig.Results().At(i).Type())) } } retType := "{" for i := 0; i < len(r.Results); i++ { res := r.Results[i] if i > 0 { retType = retType | ", " } if i < len(expectTypes) { retType = retType | expectTypes[i] } else { retType = retType | e.llvmType(res.SSAType()) } } retType = retType | "}" prev := "undef" for i := 0; i < len(r.Results); i++ { res := r.Results[i] valType := e.llvmType(res.SSAType()) valOp := e.operand(res) elemType := valType if i < len(expectTypes) { elemType = expectTypes[i] if valOp == "null" && elemType != "ptr" { valOp = "zeroinitializer" } else if (elemType == "double" || elemType == "float") && isConstOperand(valOp) { valOp = ensureFloatLit(valOp) } else if (elemType == "double" || elemType == "float") && e.intBits(valType) > 0 { valOp = e.intToFloat(valOp, valType, elemType) } else { valOp = e.coerceInt(valOp, valType, elemType) } } e.nextReg++ cur := "%rv" | irItoa(e.nextReg) e.w(" ") e.w(cur) e.w(" = insertvalue ") e.w(retType) e.w(" ") e.w(prev) e.w(", ") e.w(elemType) e.w(" ") e.w(valOp) e.w(", ") e.w(irItoa(i)) e.w("\n") prev = cur } e.w(" ret ") e.w(retType) e.w(" ") e.w(prev) e.w("\n") } func (e *irEmitter) emitJump(j *SSAJump) { blk := j.InstrBlock() if blk == nil { return } if len(blk.Succs) > 0 { e.w(" br label ") e.w(e.blockLabel(blk.Succs[0])) e.w("\n") } } func isComparisonOp(op SSAOp) bool { return op == OpEql || op == OpNeq || op == OpLss || op == OpLeq || op == OpGtr || op == OpGeq } func (e *irEmitter) emitIf(i *SSAIf) { blk := i.InstrBlock() if i.Cond == nil { if len(blk.Succs) >= 2 { e.w(" br label ") e.w(e.blockLabel(blk.Succs[1])) e.w("\n") } else { e.w(" unreachable\n") } return } cond := e.operand(i.Cond) condType := e.llvmType(i.Cond.SSAType()) if at, ok := e.allocTypes[i.Cond]; ok { condType = at } if bop, ok := i.Cond.(*SSABinOp); ok && isComparisonOp(bop.Op) { condType = "i1" } if condType != "i1" && condType != "" && condType != "void" { e.nextReg++ truncReg := "%ift" | irItoa(e.nextReg) if condType == "ptr" { e.w(" ") ; e.w(truncReg) ; e.w(" = icmp ne ptr ") ; e.w(cond) ; e.w(", null\n") } else if len(condType) > 0 && condType[0] == '{' { e.nextReg++ extReg := "%ife" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(condType) ; e.w(" ") ; e.w(cond) ; e.w(", 0\n") e.w(" ") ; e.w(truncReg) ; e.w(" = icmp ne ptr ") ; e.w(extReg) ; e.w(", null\n") } else { e.w(" ") ; e.w(truncReg) ; e.w(" = trunc ") ; e.w(condType) ; e.w(" ") ; e.w(cond) ; e.w(" to i1\n") } cond = truncReg } if len(blk.Succs) >= 2 { e.w(" br i1 ") e.w(cond) e.w(", label ") e.w(e.blockLabel(blk.Succs[0])) e.w(", label ") e.w(e.blockLabel(blk.Succs[1])) e.w("\n") } } func (e *irEmitter) emitConvert(c *SSAConvert) { reg := e.regName(c) srcType := e.llvmType(c.X.SSAType()) dstType := e.llvmType(c.SSAType()) val := e.operand(c.X) if srcType != "ptr" { resolved := e.resolvedType(c.X, srcType) if resolved != srcType { srcType = resolved } } if srcType == "void" || c.X.SSAType() == nil { if dstType == "ptr" { e.valName[c] = "null" } else { e.valName[c] = "zeroinitializer" } return } if srcType == dstType { e.valName[c] = val e.allocTypes[c] = srcType return } srcIsInt := false if b, ok := safeUnderlying(c.X.SSAType()).(*Basic); ok { srcIsInt = b.Info()&IsInteger != 0 } if !srcIsInt && len(srcType) > 0 && srcType[0] == 'i' { srcIsInt = true } if e.isStringLike(c.SSAType()) && srcIsInt { if k, ok := c.X.(*SSAConst); ok { rv := int64(0) if ci, ok2 := k.val.(constInt); ok2 { rv = ci.v } s := runeToUTF8(rune(rv)) idx := e.addStringConst(s) ipt := e.intptrType() slen := irItoa64(int64(len(s))) e.valName[c] = "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }" return } e.declareRuntime("runtime.stringFromUnicode", e.sliceType(), "i32, ptr") srcVal := val if srcType != "i32" { e.nextReg++ srcVal = "%cv" | irItoa(e.nextReg) if e.typeBits(c.X.SSAType()) < 32 { e.w(" ") ; e.w(srcVal) ; e.w(" = sext ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(" to i32\n") } else if e.typeBits(c.X.SSAType()) > 32 { e.w(" ") ; e.w(srcVal) ; e.w(" = trunc ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(" to i32\n") } } e.w(" ") ; e.w(reg) ; e.w(" = call ") ; e.w(e.sliceType()) ; e.w(" @runtime.stringFromUnicode(i32 ") ; e.w(srcVal) ; e.w(", ptr null)\n") return } op := e.conversionOp(c.X.SSAType(), c.SSAType()) srcBitsLLVM := e.intBits(srcType) dstBitsLLVM := e.intBits(dstType) if (op == "sext" || op == "zext") && srcBitsLLVM > 0 && dstBitsLLVM > 0 && srcBitsLLVM > dstBitsLLVM { op = "trunc" } else if op == "trunc" && srcBitsLLVM > 0 && dstBitsLLVM > 0 && srcBitsLLVM < dstBitsLLVM { op = "sext" } srcIsFloat := srcType == "double" || srcType == "float" dstIsFloat := dstType == "double" || dstType == "float" if op == "trunc" && srcIsFloat && !dstIsFloat { op = "fptosi" } else if op == "trunc" && !srcIsFloat && dstIsFloat { op = "sitofp" } else if (op == "sext" || op == "zext") && !srcIsFloat && dstIsFloat { op = "sitofp" } else if (op == "sext" || op == "zext") && srcIsFloat && !dstIsFloat { op = "fptosi" } else if op == "bitcast" && srcIsFloat != dstIsFloat { if srcIsFloat { op = "fptosi" } else { op = "sitofp" } } else if (op == "sext" || op == "zext" || op == "trunc") && srcIsFloat && dstIsFloat { if e.intBits(srcType) < e.intBits(dstType) { op = "fpext" } else { op = "fptrunc" } } if op == "ptrtoint" && e.intBits(dstType) == 0 { if dstType == e.ifaceType() { typeid := e.typeIDGlobal(c.X.SSAType()) t1 := e.nextReg2("cv") 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(val) ; e.w(", 1\n") } else { e.valName[c] = "zeroinitializer" } return } if op == "inttoptr" && e.intBits(srcType) == 0 { if srcType == e.ifaceType() { e.nextReg++ r := "%cv" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") e.valName[c] = r } else { e.valName[c] = "null" } return } e.w(" ") e.w(reg) e.w(" = ") e.w(op) e.w(" ") e.w(srcType) e.w(" ") e.w(val) e.w(" to ") e.w(dstType) e.w("\n") } func (e *irEmitter) conversionOp(from, to Type) string { fromBits := e.typeBits(from) toBits := e.typeBits(to) fromFloat := false toFloat := false fromSigned := true if b, ok := safeUnderlying(from).(*Basic); ok { fromFloat = b.Info()&IsFloat != 0 if b.Info()&IsUnsigned != 0 { fromSigned = false } } if b, ok := safeUnderlying(to).(*Basic); ok { toFloat = b.Info()&IsFloat != 0 } if fromFloat && toFloat { if fromBits < toBits { return "fpext" } return "fptrunc" } if fromFloat && !toFloat { if fromSigned { return "fptosi" } return "fptoui" } if !fromFloat && toFloat { if fromSigned { return "sitofp" } return "uitofp" } _, fromPtr := safeUnderlying(from).(*Pointer) _, toPtr := safeUnderlying(to).(*Pointer) if !fromPtr && e.llvmType(from) == "ptr" { fromPtr = true } if !toPtr && e.llvmType(to) == "ptr" { toPtr = true } if fromPtr && !toPtr { return "ptrtoint" } if !fromPtr && toPtr { return "inttoptr" } if fromBits < toBits { if fromSigned { return "sext" } return "zext" } if fromBits > toBits { return "trunc" } return "bitcast" } func (e *irEmitter) typeBits(t Type) int { if t == nil { return 0 } switch t := safeUnderlying(t).(type) { case *Basic: switch t.Kind() { case Bool: return 1 case Int8, Uint8: return 8 case Int16, Uint16: return 16 case Int32, Uint32: return 32 case Int64, Uint64: return 64 case Float32: return 32 case Float64: return 64 case UntypedInt, UntypedRune: return 32 case UntypedFloat: return 64 case UnsafePointer: return e.ptrBits } case *Pointer: return e.ptrBits } return 0 } func (e *irEmitter) emitChangeType(c *SSAChangeType) { srcType := e.llvmType(c.X.SSAType()) dstType := e.llvmType(c.SSAType()) if at, ok := e.allocTypes[c.X]; ok && at != "ptr" && at != "void" { srcType = at } if srcType == dstType || (srcType == "ptr" && dstType == "ptr") { e.valName[c] = e.operand(c.X) return } reg := e.regName(c) val := e.operand(c.X) e.nextReg++ tmp := "%ct" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(dstType) ; e.w("\n") e.w(" store ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(dstType) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") } func (e *irEmitter) emitFieldAddr(f *SSAFieldAddr) { reg := e.regName(f) baseType := e.llvmType(f.X.SSAType()) if p, ok := safeUnderlying(f.X.SSAType()).(*Pointer); ok && p.Elem() != nil { elem := p.Elem() if p2, ok2 := safeUnderlying(elem).(*Pointer); ok2 && p2.Elem() != nil { baseType = e.llvmType(p2.Elem()) } else { baseType = e.llvmType(elem) } } if at, ok := e.allocTypes[f.X]; ok && at != "ptr" && at != "void" { baseType = at } base := e.operand(f.X) if uop, ok := f.X.(*SSAUnOp); ok { _, isFreeVar := uop.X.(*SSAFreeVar) addrType := e.llvmType(uop.X.SSAType()) useSource := false if p, ok2 := safeUnderlying(uop.X.SSAType()).(*Pointer); ok2 && p.Elem() != nil { elem := p.Elem() if _, ok3 := safeUnderlying(elem).(*Pointer); ok3 { // double-pointer: alloca holds **T, keep the loaded *T as base } else { baseType = e.llvmType(elem) useSource = true } } if useSource && !isFreeVar && addrType == "ptr" && baseType != "ptr" && baseType != "void" { base = e.operand(uop.X) } } if baseType == "ptr" || baseType == "void" { e.w(" ") ; e.w(reg) ; e.w(" = getelementptr inbounds i8, ptr ") ; e.w(base) e.w(", i32 0\n") return } e.w(" ") e.w(reg) e.w(" = getelementptr inbounds ") e.w(baseType) e.w(", ptr ") e.w(base) e.w(", i32 0, i32 ") e.w(irItoa(f.Field)) e.w("\n") } func (e *irEmitter) emitIndexAddr(idx *SSAIndexAddr) { reg := e.regName(idx) elemType := e.llvmType(idx.SSAType()) if p, ok := safeUnderlying(idx.SSAType()).(*Pointer); ok { elemType = e.llvmType(p.Elem()) } base := e.operand(idx.X) index := e.operand(idx.Index) baseType := e.llvmType(idx.X.SSAType()) resolvedBase := e.resolvedType(idx.X, baseType) _, isSlice := safeUnderlying(idx.X.SSAType()).(*Slice) if !isSlice { if b, ok := safeUnderlying(idx.X.SSAType()).(*Basic); ok && b.Info()&IsString != 0 { isSlice = true } } if at, ok4 := e.allocTypes[idx.X]; ok4 && len(at) > 0 && at[0] == '[' { isSlice = false } else if !isSlice && (baseType == e.sliceType() || resolvedBase == e.sliceType()) { isSlice = true } if isSlice && elemType == "void" { elemType = "i8" } if isSlice { e.nextReg++ dataPtr := "%sp" | irItoa(e.nextReg) e.w(" ") e.w(dataPtr) e.w(" = extractvalue ") e.w(e.sliceType()) e.w(" ") e.w(base) e.w(", 0\n") e.w(" ") e.w(reg) e.w(" = getelementptr inbounds ") e.w(elemType) e.w(", ptr ") e.w(dataPtr) e.w(", ") e.w(e.llvmType(idx.Index.SSAType())) e.w(" ") e.w(index) e.w("\n") return } arr, isArray := safeUnderlying(idx.X.SSAType()).(*Array) if !isArray { if at, ok4 := e.allocTypes[idx.X]; ok4 && len(at) > 0 && at[0] == '[' { isArray = true } } if !isArray { if load, ok4 := idx.X.(*SSAUnOp); ok4 && load.Op == OpMul { if at, ok5 := e.allocTypes[load.X]; ok5 && len(at) > 0 && at[0] == '[' { isArray = true e.allocTypes[idx.X] = at allocBase := e.operand(load.X) e.w(" ") ; e.w(reg) ; e.w(" = getelementptr inbounds ") e.w(at) ; e.w(", ptr ") ; e.w(allocBase) ; e.w(", i32 0, ") e.w(e.llvmType(idx.Index.SSAType())) ; e.w(" ") ; e.w(index) ; e.w("\n") aet := e.arrayElemType(at) if aet != "" { e.setRegType(idx, reg, aet) } return } } } if isArray { arrType := e.llvmType(idx.X.SSAType()) if at, ok4 := e.allocTypes[idx.X]; ok4 && len(at) > 0 && at[0] == '[' { arrType = at } _, isGlobal := idx.X.(*SSAGlobal) _, isAlloc := idx.X.(*SSAAlloc) if isGlobal || isAlloc { _ = arr e.w(" ") ; e.w(reg) ; e.w(" = getelementptr inbounds ") e.w(arrType) ; e.w(", ptr ") ; e.w(base) ; e.w(", i32 0, ") e.w(e.llvmType(idx.Index.SSAType())) ; e.w(" ") ; e.w(index) ; e.w("\n") return } e.nextReg++ arrPtr := "%ai" | irItoa(e.nextReg) e.w(" ") ; e.w(arrPtr) ; e.w(" = alloca ") ; e.w(arrType) ; e.w("\n") e.w(" store ") ; e.w(arrType) ; e.w(" ") ; e.w(base) ; e.w(", ptr ") ; e.w(arrPtr) ; e.w("\n") e.w(" ") ; e.w(reg) ; e.w(" = getelementptr inbounds ") e.w(arrType) ; e.w(", ptr ") ; e.w(arrPtr) ; e.w(", i32 0, ") e.w(e.llvmType(idx.Index.SSAType())) ; e.w(" ") ; e.w(index) ; e.w("\n") aet := e.arrayElemType(arrType) if aet != "" { e.setRegType(idx, reg, aet) } return } if len(elemType) > 0 && elemType[0] == '[' { aet := e.arrayElemType(elemType) e.w(" ") ; e.w(reg) ; e.w(" = getelementptr inbounds ") e.w(elemType) ; e.w(", ptr ") ; e.w(base) ; e.w(", i32 0, ") e.w(e.llvmType(idx.Index.SSAType())) ; e.w(" ") ; e.w(index) ; e.w("\n") e.setRegType(idx, reg, aet) return } e.w(" ") e.w(reg) e.w(" = getelementptr inbounds ") e.w(elemType) e.w(", ptr ") e.w(base) e.w(", ") e.w(e.llvmType(idx.Index.SSAType())) e.w(" ") e.w(index) e.w("\n") } func (e *irEmitter) emitExtract(ex *SSAExtract) { reg := e.regName(ex) tupType := e.llvmType(ex.Tuple.SSAType()) if at, ok := e.allocTypes[ex.Tuple]; ok { tupType = at } if n, ok := ex.Tuple.(*SSANext); ok { rangeInstr := n.Iter.(*SSARange) if mt, ok2 := safeUnderlying(rangeInstr.X.SSAType()).(*TCMap); ok2 { tupType = "{i1, " | e.llvmType(mt.Key()) | ", " | e.llvmType(mt.Elem()) | "}" } else if arr, ok2 := safeUnderlying(rangeInstr.X.SSAType()).(*Array); ok2 { tupType = "{i1, i32, " | e.llvmType(arr.Elem()) | "}" } else { et := "i32" if sl, ok2 := safeUnderlying(rangeInstr.X.SSAType()).(*Slice); ok2 { et = e.llvmType(sl.Elem()) } tupType = "{i1, i32, " | et | "}" } } val := e.operand(ex.Tuple) // Track extracted element type for downstream alloc/store consistency extractedType := extractTupleField(tupType, ex.Index) if extractedType != "" { ssaType := e.llvmType(ex.SSAType()) if extractedType != ssaType { e.allocTypes[ex] = extractedType } } if tupType == "ptr" || tupType == "void" { elemType := e.llvmType(ex.SSAType()) if elemType == "void" { elemType = "ptr" } e.nextReg++ castReg := "%ev" | irItoa(e.nextReg) e.w(" ") ; e.w(castReg) ; e.w(" = getelementptr inbounds i8, ptr ") ; e.w(val) ; e.w(", i32 0\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(castReg) ; e.w("\n") e.allocTypes[ex] = elemType return } e.w(" ") e.w(reg) e.w(" = extractvalue ") e.w(tupType) e.w(" ") e.w(val) e.w(", ") e.w(irItoa(ex.Index)) e.w("\n") } func extractTupleField(tupType string, index int) string { if len(tupType) < 3 || tupType[0] != '{' { return "" } inner := tupType[1 : len(tupType)-1] depth := 0 field := 0 start := 0 for i := 0; i < len(inner); i++ { c := inner[i] if c == '{' { depth++ } else if c == '}' { depth-- } else if c == ',' && depth == 0 { if field == index { s := inner[start:i] for len(s) > 0 && s[0] == ' ' { s = s[1:] } for len(s) > 0 && s[len(s)-1] == ' ' { s = s[:len(s)-1] } return s } field++ start = i + 1 } } if field == index { s := inner[start:] for len(s) > 0 && s[0] == ' ' { s = s[1:] } for len(s) > 0 && s[len(s)-1] == ' ' { s = s[:len(s)-1] } return s } return "" } func (e *irEmitter) sextToIpt(val SSAValue, op string) string { ipt := e.intptrType() if val == nil { return op } valType := e.llvmType(val.SSAType()) if valType == ipt { return op } e.nextReg++ ext := "%sx" | irItoa(e.nextReg) extOp := "sext" if b, ok := safeUnderlying(val.SSAType()).(*Basic); ok && b.Info()&IsUnsigned != 0 { extOp = "zext" } e.w(" ") ; e.w(ext) ; e.w(" = ") ; e.w(extOp) ; e.w(" ") ; e.w(valType) ; e.w(" ") ; e.w(op) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") return ext } func (e *irEmitter) emitMakeSlice(m *SSAMakeSlice) { reg := e.regName(m) ipt := e.intptrType() sty := e.sliceType() lenOp := e.sextToIpt(m.Len, e.operand(m.Len)) capOp := lenOp if m.Cap != nil { capOp = e.sextToIpt(m.Cap, e.operand(m.Cap)) } var dataPtr string if m.Data != nil { dataPtr = e.operand(m.Data) } else { elemType := "i8" if sl, ok := safeUnderlying(m.SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } e.nextReg++ elemSz := "%ms" | irItoa(e.nextReg) e.w(" ") e.w(elemSz) e.w(" = ptrtoint ptr getelementptr (") e.w(elemType) e.w(", ptr null, i32 1) to ") e.w(ipt) e.w("\n") e.nextReg++ allocSz := "%ms" | irItoa(e.nextReg) e.w(" ") e.w(allocSz) e.w(" = mul ") e.w(ipt) e.w(" ") e.w(elemSz) e.w(", ") e.w(capOp) e.w("\n") e.nextReg++ dataPtr = "%ms" | irItoa(e.nextReg) e.w(" ") e.w(dataPtr) e.w(" = call ptr @runtime.alloc(") e.w(ipt) e.w(" ") e.w(allocSz) e.w(", ptr null, ptr undef)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") } e.nextReg++ s1 := "%ms" | irItoa(e.nextReg) e.w(" ") e.w(s1) e.w(" = insertvalue ") e.w(sty) e.w(" undef, ptr ") e.w(dataPtr) e.w(", 0\n") e.nextReg++ s2 := "%ms" | irItoa(e.nextReg) e.w(" ") e.w(s2) e.w(" = insertvalue ") e.w(sty) e.w(" ") e.w(s1) e.w(", ") e.w(ipt) e.w(" ") e.w(lenOp) e.w(", 1\n") e.w(" ") e.w(reg) e.w(" = insertvalue ") e.w(sty) e.w(" ") e.w(s2) e.w(", ") e.w(ipt) e.w(" ") e.w(capOp) e.w(", 2\n") } func (e *irEmitter) emitSliceOp(s *SSASlice) { reg := e.regName(s) ipt := e.intptrType() sty := e.sliceType() src := e.operand(s.X) var oldPtr, oldLen, oldCap string srcType := safeUnderlying(s.X.SSAType()) if p, ok := srcType.(*Pointer); ok && p.Elem() != nil { if arr, ok2 := safeUnderlying(p.Elem()).(*Array); ok2 { oldPtr = src oldLen = irItoa64(arr.Len()) oldCap = oldLen srcType = nil } } if arr, ok := srcType.(*Array); ok { arrType := e.llvmType(s.X.SSAType()) e.nextReg++ tmp := "%sl" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = alloca ") ; e.w(arrType) ; e.w("\n") e.w(" store ") ; e.w(arrType) ; e.w(" ") ; e.w(src) ; e.w(", ptr ") ; e.w(tmp) ; e.w("\n") oldPtr = tmp oldLen = irItoa64(arr.Len()) oldCap = oldLen } else if srcType != nil { e.nextReg++ oldPtr = "%sl" | irItoa(e.nextReg) e.w(" ") e.w(oldPtr) e.w(" = extractvalue ") e.w(sty) e.w(" ") e.w(src) e.w(", 0\n") e.nextReg++ oldLen = "%sl" | irItoa(e.nextReg) e.w(" ") e.w(oldLen) e.w(" = extractvalue ") e.w(sty) e.w(" ") e.w(src) e.w(", 1\n") e.nextReg++ oldCap = "%sl" | irItoa(e.nextReg) e.w(" ") e.w(oldCap) e.w(" = extractvalue ") e.w(sty) e.w(" ") e.w(src) e.w(", 2\n") } low := "0" if s.Low != nil { low = e.sliceIdxToIpt(s.Low, ipt) } high := oldLen if s.High != nil { high = e.sliceIdxToIpt(s.High, ipt) } maxCap := oldCap if s.Max != nil { maxCap = e.sliceIdxToIpt(s.Max, ipt) } elemType := "i8" if sl, ok := safeUnderlying(s.X.SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } else if ar, ok := safeUnderlying(s.X.SSAType()).(*Array); ok { elemType = e.llvmType(ar.Elem()) } else if p, ok := safeUnderlying(s.X.SSAType()).(*Pointer); ok && p.Elem() != nil { if ar, ok2 := safeUnderlying(p.Elem()).(*Array); ok2 { elemType = e.llvmType(ar.Elem()) } } e.nextReg++ newPtr := "%sl" | irItoa(e.nextReg) e.w(" ") e.w(newPtr) e.w(" = getelementptr inbounds ") e.w(elemType) e.w(", ptr ") e.w(oldPtr) e.w(", ") e.w(ipt) e.w(" ") e.w(low) e.w("\n") e.nextReg++ newLen := "%sl" | irItoa(e.nextReg) e.w(" ") e.w(newLen) e.w(" = sub ") e.w(ipt) e.w(" ") e.w(high) e.w(", ") e.w(low) e.w("\n") e.nextReg++ newCap := "%sl" | irItoa(e.nextReg) e.w(" ") e.w(newCap) e.w(" = sub ") e.w(ipt) e.w(" ") e.w(maxCap) e.w(", ") e.w(low) e.w("\n") e.nextReg++ s1 := "%sl" | irItoa(e.nextReg) e.w(" ") e.w(s1) e.w(" = insertvalue ") e.w(sty) e.w(" undef, ptr ") e.w(newPtr) e.w(", 0\n") e.nextReg++ s2 := "%sl" | irItoa(e.nextReg) e.w(" ") e.w(s2) e.w(" = insertvalue ") e.w(sty) e.w(" ") e.w(s1) e.w(", ") e.w(ipt) e.w(" ") e.w(newLen) e.w(", 1\n") e.w(" ") e.w(reg) e.w(" = insertvalue ") e.w(sty) e.w(" ") e.w(s2) e.w(", ") e.w(ipt) e.w(" ") e.w(newCap) e.w(", 2\n") } func (e *irEmitter) sliceIdxToIpt(val SSAValue, ipt string) string { operandStr := e.operand(val) valType := e.llvmType(val.SSAType()) if valType == ipt { return operandStr } e.nextReg++ ext := "%sl" | irItoa(e.nextReg) op := "sext" if b, ok2 := safeUnderlying(val.SSAType()).(*Basic); ok2 && b.Info()&IsUnsigned != 0 { op = "zext" } e.w(" ") ; e.w(ext) ; e.w(" = ") ; e.w(op) ; e.w(" ") ; e.w(valType) ; e.w(" ") ; e.w(operandStr) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") return ext } 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) emitMakeInterface(m *SSAMakeInterface) { reg := e.regName(m) val := e.operand(m.X) valType := e.llvmType(m.X.SSAType()) if _, isAlloc := m.X.(*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 == "ptr" { valPtr = val } else if e.isScalarType(valType) { ipt := e.intptrType() ext := e.nextReg2("mi") if valType == "i1" || valType == "i8" || valType == "i16" || valType == "i32" { 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") 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" { ext = e.nextReg2("mi") e.w(" ") ; e.w(ext) ; e.w(" = bitcast double ") ; e.w(val) ; 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") } 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 Type) string { name := e.reflectTypeName(t) if e.typeIDs == nil { e.typeIDs = map[string]int{} } if _, ok := e.typeIDs[name]; !ok { e.typeIDNext++ e.typeIDs[name] = e.typeIDNext } if hasPrefix(name, "reflect/types.type:") { return "@\"" | name | "\"" } return "@" | name } func (e *irEmitter) reflectTypeName(t Type) string { if b, ok := t.(*Basic); ok { switch b.Kind() { case Bool, UntypedBool: return "reflect/types.type:basic:bool" case Int8: return "reflect/types.type:basic:int8" case Int16: return "reflect/types.type:basic:int16" case Int32, UntypedInt, UntypedRune: return "reflect/types.type:basic:int32" case Int64: return "reflect/types.type:basic:int64" case Uint8: return "reflect/types.type:basic:uint8" case Uint16: return "reflect/types.type:basic:uint16" case Uint32: return "reflect/types.type:basic:uint32" case Uint64: return "reflect/types.type:basic:uint64" case Float32, UntypedFloat: return "reflect/types.type:basic:float32" case Float64: return "reflect/types.type:basic:float64" case TCString, UntypedString: return "reflect/types.type:basic:bytes" case UnsafePointer: return "reflect/types.type:basic:uintptr" } } if named, ok := t.(*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.(*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.(*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.(*Slice); ok { inner := e.reflectTypeName(sl.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.(*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.(*TypeName) if !ok2 || tn.Type() == nil { continue } isPtrRecv := fn.object != nil && fn.object.HasPtrRecv() recvT := tn.Type() if isPtrRecv { recvT = 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).(*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: 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).(*TypeName) if !ok7 || tn3.Type() == nil { continue } named3, ok8 := tn3.typ.(*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 Type, methodName string, depth int) ([]int, *SSAFunction, Type) { if depth > 5 { return nil, nil, nil } st, ok := safeUnderlying(t).(*TCStruct) if !ok { 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.(*Named); ok2 && en.Obj() != nil { embedName = en.Obj().Name() } if embedName == "" { continue } embedMName := embedName | "." | methodName if fn, ok2 := e.pkg.Members[embedMName].(*SSAFunction); ok2 { return []int{fi}, fn, embedType } sub, fn, embedT := e.findEmbedMethod(embedType, methodName, depth+1) if fn != nil { return append([]int{fi}, sub...), fn, embedT } } return nil, nil, nil } type ifaceImpl struct { fn *SSAFunction recvType Type ptrRecv bool embedField int embedType Type embedChain []int 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 *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 = "ptr" } params = params | ", " | flattenTypeStr(argT) } 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 *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 = []int{impl.embedField} } outerType := impl.recvType if pt, ok := outerType.(*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 := safeUnderlying(curType).(*TCStruct) if ok && idx < st.NumFields() { curType = st.Field(idx).Type() } } return cur } type invokeArg struct { typ string val string } func (e *irEmitter) prepareInvokeArgs(inv *SSAInvoke, impl ifaceImpl) []invokeArg { var sig *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 != "" && 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 *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.(*Pointer); ok { recvType = pt.Elem() } recvLLVM = e.llvmType(recvType) } if recvLLVM == "ptr" { recv = callRecv } else if e.isScalarType(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.(*Pointer); ok { recvType = pt.Elem() } recvLLVM = e.llvmType(recvType) } if recvLLVM == "ptr" { recv = callRecv } else if e.isScalarType(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 *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 = valPtr } else if e.isScalarType(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.isScalarType(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" { tr := e.nextReg2("ta") e.w(" ") ; e.w(tr) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(raw) ; e.w(" to i32\n") bc := e.nextReg2("ta") e.w(" ") ; e.w(bc) ; e.w(" = bitcast i32 ") ; e.w(tr) ; 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 *SSAMakeMap) { reg := e.regName(m) ipt := e.intptrType() var mt *TCMap if okv, okok := safeUnderlying(m.SSAType()).(*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") } func (e *irEmitter) emitMapUpdate(m *SSAMapUpdate) { mapVal := e.operand(m.Map) keyVal := e.operand(m.Key) valVal := e.operand(m.Value) mapType := m.Map.SSAType() if pt, ok := safeUnderlying(mapType).(*Pointer); ok { mapType = pt.Elem() } var mt *TCMap if okv, okok := safeUnderlying(mapType).(*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") 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()) { kd := e.nextReg2("mu") kl := e.nextReg2("mu") kc := e.nextReg2("mu") e.w(" ") ; e.w(kd) ; e.w(" = extractvalue {ptr, i64, i64} ") ; e.w(keyVal) ; e.w(", 0\n") e.w(" ") ; e.w(kl) ; e.w(" = extractvalue {ptr, i64, i64} ") ; e.w(keyVal) ; e.w(", 1\n") e.w(" ") ; e.w(kc) ; e.w(" = extractvalue {ptr, i64, i64} ") ; 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(", i64 ") ; e.w(kl) e.w(", i64 ") ; e.w(kc) e.w(", ptr ") ; e.w(valAlloca) ; e.w(")\n") e.declareRuntime("runtime.hashmapContentSet", "void", "ptr, ptr, i64, i64, 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 *SSALookup) { reg := e.regName(l) ipt := e.intptrType() mapVal := e.operand(l.X) keyVal := e.operand(l.Index) var mt *TCMap if okv, okok := safeUnderlying(l.X.SSAType()).(*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()) { kd := e.nextReg2("ml") kl := e.nextReg2("ml") kc := e.nextReg2("ml") e.w(" ") ; e.w(kd) ; e.w(" = extractvalue {ptr, i64, i64} ") ; e.w(keyVal) ; e.w(", 0\n") e.w(" ") ; e.w(kl) ; e.w(" = extractvalue {ptr, i64, i64} ") ; e.w(keyVal) ; e.w(", 1\n") e.w(" ") ; e.w(kc) ; e.w(" = extractvalue {ptr, i64, i64} ") ; 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(", i64 ") ; e.w(kl) e.w(", i64 ") ; 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, i64, i64, 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 Type) bool { if t == nil { return false } if b, ok := safeUnderlying(t).(*Basic); ok { return b.Info()&IsString != 0 } return false } func (e *irEmitter) emitMakeClosure(m *SSAMakeClosure) { reg := e.regName(m) var fn *SSAFunction if okv, okok := m.Fn.(*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") 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") 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") } func (e *irEmitter) closureBindingType(b SSAValue) string { t := e.llvmType(b.SSAType()) if t == "void" || t == "i32" { t = "ptr" } return t } func (e *irEmitter) closureContextType(bindings []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 *SSAFreeVar) string { return "ptr" } func (e *irEmitter) emitFreeVarUnpack(f *SSAFunction) { 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 %context, 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 *SSAPanic) { if c, ok := p.X.(*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 *SSARange) { reg := e.regName(r) if _, ok := safeUnderlying(r.X.SSAType()).(*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 *SSANext) { reg := e.regName(n) rangeInstr := n.Iter.(*SSARange) iterPtr := e.regName(rangeInstr) collVal := e.operand(rangeInstr.X) if mt, ok := safeUnderlying(rangeInstr.X.SSAType()).(*TCMap); ok { e.emitNextMap(reg, iterPtr, collVal, mt, n) return } if arr, ok := safeUnderlying(rangeInstr.X.SSAType()).(*Array); ok { e.emitNextArray(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 *SSARange, n *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") key := e.nextReg2("rn") e.w(" ") ; e.w(key) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(" to i32\n") 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" if sl, ok2 := safeUnderlying(rangeInstr.X.SSAType()).(*Slice); ok2 { elemType = e.llvmType(sl.Elem()) } 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 *Array, n *SSANext) { ipt := e.intptrType() arrLen := arr.Len() elemType := e.llvmType(arr.Elem()) arrType := "[" | irItoa(int(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(int(arrLen))) ; e.w("\n") key := e.nextReg2("rn") e.w(" ") ; e.w(key) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(" to i32\n") // 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) emitNextMap(reg, iterPtr, collVal string, mt *TCMap, n *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") } func (e *irEmitter) operandNoSideEffect(v SSAValue) string { if v == nil { return "zeroinitializer" } if c, ok := v.(*SSAConst); ok { return e.constOperand(c) } if n, ok := e.valName[v]; ok { return n } return "" } func (e *irEmitter) operand(v SSAValue) string { if v == nil { return "zeroinitializer" } if c, ok := v.(*SSAConst); ok { return e.constOperand(c) } if b, ok := v.(*SSABuiltin); ok { return "@runtime." | b.SSAName() } if f, ok := v.(*SSAFunction); ok { return "{ ptr null, ptr " | e.funcSymbol(f) | " }" } if g, ok := v.(*SSAGlobal); ok { e.declareExternalGlobal(g) return e.globalName(g) } return e.regName(v) } func (e *irEmitter) constOperand(c *SSAConst) string { if c.val == nil { if c.typ == nil { return "null" } typ := e.llvmType(c.typ) if typ == "ptr" { return "null" } if typ == "i1" { return "false" } return "zeroinitializer" } b := underlyingBasic(c.typ) if b != nil { switch b.kind { case Bool, UntypedBool: if cb, ok := c.val.(constBool); ok { if cb.v { return "true" } return "false" } s := c.val.String() if s == "true" { return "true" } return "false" case Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64, UntypedInt, UntypedRune: if ci, ok := c.val.(constInt); ok { v := ci.v switch b.kind { case Int8: v = int64(int8(v)) case Uint8: v = int64(uint8(v)) case Int16: v = int64(int16(v)) case Uint16: v = int64(uint16(v)) case Int32, UntypedInt, UntypedRune: v = int64(int32(v)) case Uint32: v = int64(uint32(v)) } return irItoa64(v) } if cf, ok := c.val.(constFloat); ok { return irItoa64(int64(cf.v)) } return c.val.String() case Float32, Float64, UntypedFloat: if cf, ok := c.val.(constFloat); ok { return cf.String() } return c.val.String() case TCString, UntypedString: if cs, ok := c.val.(constStr); ok { if len(cs.s) == 0 { return "zeroinitializer" } idx := e.addStringConst(cs.s) ipt := e.intptrType() slen := irItoa64(int64(len(cs.s))) return "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }" } return "zeroinitializer" } } if c.typ == nil { return c.val.String() } return "zeroinitializer" } func underlyingBasic(t Type) *Basic { if t == nil { return nil } u := safeUnderlying(t) if u == nil { return nil } b, ok := u.(*Basic) if !ok { return nil } return b } func newTCPackageWithUniverse(path, name string) *TCPackage { return &TCPackage{ path: path, name: name, scope: NewScope(Universe), } } func irParseIntWidth(t string) int { if len(t) < 2 || t[0] != 'i' { return 0 } n := 0 for i := 1; i < len(t); i++ { if t[i] < '0' || t[i] > '9' { return 0 } n = n*10 + int(t[i]-'0') } return n } func irItoa(n int) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } buf := []byte{:0:20} for n > 0 { buf = append(buf, byte('0'+n%10)) n /= 10 } if neg { buf = append(buf, '-') } for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) } func irItoa64(n int64) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } buf := []byte{:0:20} for n > 0 { buf = append(buf, byte('0'+n%10)) n /= 10 } if neg { buf = append(buf, '-') } for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) } func irFtoa(f float64) string { return "0.0" } func irParseInt64(s string) int64 { var n int64 for i := 0; i < len(s); i++ { c := s[i] if c < '0' || c > '9' { break } n = n*10 + int64(c-'0') } return n } func runeToUTF8(r rune) string { if r < 0 || r > 0x10FFFF { r = 0xFFFD } var buf [4]byte switch { case r <= 0x7F: buf[0] = byte(r) return string(buf[:1]) case r <= 0x7FF: buf[0] = byte(0xC0 | (r >> 6)) buf[1] = byte(0x80 | (r & 0x3F)) return string(buf[:2]) case r <= 0xFFFF: buf[0] = byte(0xE0 | (r >> 12)) buf[1] = byte(0x80 | ((r >> 6) & 0x3F)) buf[2] = byte(0x80 | (r & 0x3F)) return string(buf[:3]) default: buf[0] = byte(0xF0 | (r >> 18)) buf[1] = byte(0x80 | ((r >> 12) & 0x3F)) buf[2] = byte(0x80 | ((r >> 6) & 0x3F)) buf[3] = byte(0x80 | (r & 0x3F)) return string(buf[:4]) } } func scanExportPragmas(src []byte) map[string]string { result := map[string]string{} exportPrefix := []byte("//export ") funcPrefix := []byte("func ") commentPrefix := []byte("//") pendingExport := "" i := 0 for i < len(src) { nlIdx := bytes.IndexByte(src[i:], '\n') var line []byte var lineEnd int if nlIdx < 0 { line = src[i:] lineEnd = len(src) } else { line = src[i : i+nlIdx] lineEnd = i + nlIdx + 1 } trimmed := bytes.TrimSpace(line) if len(pendingExport) > 0 { if len(trimmed) == 0 || bytes.HasPrefix(trimmed, commentPrefix) { i = lineEnd continue } if bytes.HasPrefix(trimmed, funcPrefix) { rest := trimmed[5:] paren := bytes.IndexByte(rest, '(') if paren > 0 { funcName := string(bytes.TrimSpace(rest[:paren])) result[funcName] = pendingExport } } pendingExport = "" } else if bytes.HasPrefix(trimmed, exportPrefix) { pendingExport = string(bytes.TrimSpace(trimmed[9:])) } i = lineEnd } return result } func typeCheckPkg(src []byte, name string) (*TCPackage, *File) { initUniverse() shortName := name for i := len(name) - 1; i >= 0; i-- { if name[i] == '/' { shortName = name[i+1:] break } } pkg := newTCPackageWithUniverse(name, shortName) scope := pkg.Scope() src = rewriteSliceMakeLiterals(src) src = rewriteChanMakeLiterals(src) src = stripDuplicatePackageClauses(src) if len(src) == 0 { return nil, nil } parseErrors = nil constValMap = nil errh := func(err error) { parseErrors = append(parseErrors, err.Error()) } tcPkgSrc = src compileExportMap = scanExportPragmas(src) writeStr(2, " parse-start srcLen=" | simpleItoa(len(src)) | "\n") file, _ := ParseBytes(NewFileBase(name|".mx"), src, errh, nil, 0) writeStr(2, " parse-done\n") if file == nil { return nil, nil } for _, d := range file.DeclList { switch d := d.(type) { case *ImportDecl: if d.Path == nil { continue } path := d.Path.Value if len(path) >= 2 && path[0] == '"' { path = path[1 : len(path)-1] } ensureImportRegistry() imported := importRegistry[path] if imported == nil { continue } if path == "unsafe" && imported.Scope().Lookup("Pointer") == nil { imported.Scope().Insert(NewTypeName(imported, "Pointer", Typ[UnsafePointer])) } localName := imported.Name() if d.LocalPkgName != nil { localName = d.LocalPkgName.Value } scope.Insert(NewPkgName(pkg, localName, imported)) case *VarDecl: for _, n := range d.NameList { scope.Insert(NewTCVar(pkg, n.Value, nil)) } case *FuncDecl: if d.Recv == nil && d.Name.Value != "init" { scope.Insert(NewTCFunc(pkg, d.Name.Value, nil)) } case *TypeDecl: scope.Insert(NewTypeName(pkg, d.Name.Value, nil)) case *ConstDecl: for _, n := range d.NameList { scope.Insert(NewTCConst(pkg, n.Value, nil, nil)) } } } writeStr(2, " scope-pass-done\n") var curConstGroup *Group var prevConstValues Expr var prevConstType Expr iotaVal := int64(-1) for _, d := range file.DeclList { if td, ok := d.(*TypeDecl); ok { obj := scope.Lookup(td.Name.Value) if obj != nil { if tn, ok2 := obj.(*TypeName); ok2 { NewNamed(tn, nil) } } } } for _, d := range file.DeclList { if cd, ok := d.(*ConstDecl); ok { if cd.Group == nil || cd.Group != curConstGroup { curConstGroup = cd.Group iotaVal = int64(0) prevConstValues = nil prevConstType = nil } else { iotaVal++ } valExpr := cd.Values typeExpr := cd.Type if valExpr == nil { valExpr = prevConstValues } if typeExpr == nil && cd.Type == nil { typeExpr = prevConstType } if cd.Values != nil { prevConstValues = cd.Values } if cd.Type != nil { prevConstType = cd.Type } typ := tcResolveNameInline(typeExpr, scope) if typ == nil && valExpr != nil { typ = tcInferTypeFromExpr(valExpr, scope) } var val ConstVal if valExpr != nil { val = tcEvalConstExpr(valExpr, scope, iotaVal) } if typ == nil && val != nil { typ = untypedTypeOfCV(val) } for _, n := range cd.NameList { if val != nil { if ci, ok2 := val.(constInt); ok2 { if constValMap == nil { constValMap = map[string]int64{} } constValMap[n.Value] = ci.v } } obj := scope.Lookup(n.Value) if obj != nil { if c, ok := obj.(*TCConst); ok { c.typ = typ c.val = val } } } } } writeStr(2, " const-pass-done\n") for _, d := range file.DeclList { if td, ok := d.(*TypeDecl); ok { obj := scope.Lookup(td.Name.Value) if obj != nil { if tn, ok2 := obj.(*TypeName); ok2 { named, ok3 := tn.typ.(*Named) if ok3 { typ := tcResolveNameInline(td.Type, scope) named.SetUnderlying(typ) } } } } } writeStr(2, " type-pass-done\n") for _, d := range file.DeclList { switch d := d.(type) { case *VarDecl: typ := tcResolveNameInline(d.Type, scope) if arr, ok := typ.(*Array); ok && arr.Len() < 0 && d.Values != nil { if cl, ok2 := d.Values.(*CompositeLit); ok2 { typ = NewArray(arr.Elem(), int64(len(cl.ElemList))) } } if typ == nil && d.Values != nil { typ = tcInferTypeFromExpr(d.Values, scope) } for _, n := range d.NameList { obj := scope.Lookup(n.Value) if obj != nil { if v, ok := obj.(*TCVar); ok { v.typ = typ } } } case *FuncDecl: if d.Recv == nil && d.Name.Value != "init" { if len(d.TParamList) > 0 { if genericFuncDecls == nil { genericFuncDecls = map[string]*FuncDecl{} } genericFuncDecls[name+"."+d.Name.Value] = d if genericPkgScopes == nil { genericPkgScopes = map[string]*Scope{} } genericPkgScopes[name] = pkg.Scope() continue } sig := tcResolveFuncInline(d.Type, scope) obj := scope.Lookup(d.Name.Value) if obj != nil { if fn, ok := obj.(*TCFunc); ok && sig != nil { fn.typ = sig } } } } } writeStr(2, " var-func-pass-done\n") for _, d := range file.DeclList { if fd, ok := d.(*FuncDecl); ok && fd.Recv != nil { recvType := tcResolveRecvType(fd.Recv, scope) if recvType == nil { continue } sig := tcResolveFuncInlineWithRecv(fd.Type, fd.Recv, scope) fn := NewTCFunc(pkg, fd.Name.Value, sig) isPtr := false var named *Named if pt, ok := recvType.(*Pointer); ok { if okv, okok := pt.Elem().(*Named); okok { named = okv } isPtr = true } else { if okv, okok := recvType.(*Named); okok { named = okv } } if named != nil { if isPtr { fn.hasPtrRecv = true } named.AddMethod(fn) } } } return pkg, file } func releasePerPkgState() { parseErrors = nil constValMap = nil compileExportMap = nil tcPkgSrc = nil genericFuncDecls = nil genericPkgScopes = nil } func TypeCheckOnly(src []byte, name string) { pkg, file := typeCheckPkg(src, name) if pkg != nil && file != nil { registerCompiledExports(pkg) } if pkg != nil { pkg.Release() } if file != nil { file.DeclList = nil } releasePerPkgState() } func CompileToIR(src []byte, name string, triple string) string { pkg, file := typeCheckPkg(src, name) if file == nil { out := "; parse error parseErrs=" | simpleItoa(len(parseErrors)) | "\n" for _, pe := range parseErrors { out = out | "; " | pe | "\n" } releasePerPkgState() return out } prog := NewSSAProgram() ssaPkg := prog.CreatePackage(pkg, []*File{file}, nil) emitter := newIREmitter(ssaPkg, triple) ir := emitter.emit() if len(ir) > 0 && ir[0] != ';' { registerCompiledExports(pkg) } emitter.releaseAfterEmit() ssaPkg.release() prog.release() if pkg != nil { pkg.Release() } file.DeclList = nil releasePerPkgState() return ir } func registerCompiledExports(pkg *TCPackage) { ensureImportRegistry() path := pkg.Path() regPkg := NewTCPackage(path, pkg.Name()) for _, name := range pkg.Scope().Names() { if len(name) == 0 || name[0] < 'A' || name[0] > 'Z' { continue } obj := pkg.Scope().Lookup(name) if obj != nil { regPkg.Scope().Insert(obj) } } importRegistry[path] = regPkg } func tcInferTypeFromExpr(e Expr, scope *Scope) Type { switch e := e.(type) { case *BasicLit: switch e.Kind { case StringLit: return Typ[TCString] case IntLit: return Typ[Int32] case FloatLit: return Typ[Float64] } case *Name: if e.Value == "true" || e.Value == "false" { return Typ[Bool] } return tcResolveNameInline(e, scope) case *CallExpr: return tcResolveNameInline(e.Fun, scope) case *CompositeLit: t := tcResolveNameInline(e.Type, scope) if arr, ok := t.(*Array); ok && arr.Len() < 0 { return NewArray(arr.Elem(), int64(len(e.ElemList))) } return t case *Operation: if e.Y == nil && e.Op == And { return NewPointer(tcInferTypeFromExpr(e.X, scope)) } } return nil } var constValMap map[string]int64 var tcPkgSrc []byte func resolveArrayLenFromSrc(p Pos, cmap map[string]int64) int64 { line := p.Line() col := p.Col() if line == 0 || col == 0 || tcPkgSrc == nil { return -1 } off := 0 curLine := uint32(1) for off < len(tcPkgSrc) && curLine < line { if tcPkgSrc[off] == '\n' { curLine++ } off++ } off += int(col) - 1 if off >= len(tcPkgSrc) || tcPkgSrc[off] != '[' { return -1 } off++ for off < len(tcPkgSrc) && tcPkgSrc[off] == ' ' { off++ } start := off for off < len(tcPkgSrc) { c := tcPkgSrc[off] if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' { off++ } else { break } } if off == start { return -1 } name := string(tcPkgSrc[start:off]) if v, ok := cmap[name]; ok { if off < len(tcPkgSrc) && tcPkgSrc[off] == '+' { off++ for off < len(tcPkgSrc) && tcPkgSrc[off] == ' ' { off++ } numStart := off for off < len(tcPkgSrc) && tcPkgSrc[off] >= '0' && tcPkgSrc[off] <= '9' { off++ } if off > numStart { addend := int64(0) for i := numStart; i < off; i++ { addend = addend*10 + int64(tcPkgSrc[i]-'0') } return v + addend } } return v } n := int64(0) isNum := true for i := start; i < off; i++ { c := tcPkgSrc[i] if c >= '0' && c <= '9' { n = n*10 + int64(c-'0') } else { isNum = false break } } if isNum && off > start { return n } return -1 } func resolveArrayLenFromConstMap(e Expr, cmap map[string]int64) int64 { line := e.Pos().Line() col := e.Pos().Col() if line == 0 || col == 0 || tcPkgSrc == nil { return -1 } curLine := uint32(1) off := 0 for off < len(tcPkgSrc) && curLine < line { if tcPkgSrc[off] == '\n' { curLine++ } off++ } off += int(col) - 1 if off >= len(tcPkgSrc) { return -1 } start := off for off < len(tcPkgSrc) { c := tcPkgSrc[off] if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' { off++ } else { break } } if off == start { return -1 } name := string(tcPkgSrc[start:off]) if v, ok := cmap[name]; ok { return v } return -1 } func tcEvalConstExpr(e Expr, scope *Scope, iotaVal int64) ConstVal { if e == nil { return nil } switch e := e.(type) { case *BasicLit: return evalBasicLitLocal(e) case *Name: if e.Value == "iota" && iotaVal >= 0 { return constInt{iotaVal} } if scope != nil { _, obj := scope.LookupParent(e.Value) if c, ok := obj.(*TCConst); ok && c.val != nil { return c.val } } if constValMap != nil { if v, ok := constValMap[e.Value]; ok { return constInt{v} } } case *Operation: if e.Y == nil { xr := tcEvalConstExpr(e.X, scope, iotaVal) if xr == nil { return nil } return evalUnaryLocal(e.Op, xr) } xr := tcEvalConstExpr(e.X, scope, iotaVal) yr := tcEvalConstExpr(e.Y, scope, iotaVal) if xr == nil || yr == nil { return nil } return evalBinaryLocal(e.Op, xr, yr) case *ParenExpr: return tcEvalConstExpr(e.X, scope, iotaVal) case *CallExpr: if id, ok := e.Fun.(*Name); ok && id.Value == "len" && len(e.ArgList) == 1 { if lit, ok2 := e.ArgList[0].(*BasicLit); ok2 && lit.Kind == StringLit { lv := evalBasicLitLocal(lit) if cs, ok3 := lv.(constStr); ok3 { return constInt{int64(len(cs.s))} } } } if sel, ok := e.Fun.(*SelectorExpr); ok { if pkg, ok2 := sel.X.(*Name); ok2 && pkg.Value == "unsafe" { switch sel.Sel.Value { case "Sizeof", "Alignof", "Offsetof": return constInt{8} } } } if len(e.ArgList) != 1 { return nil } inner := tcEvalConstExpr(e.ArgList[0], scope, iotaVal) if inner == nil { return nil } targetType := tcResolveNameInline(e.Fun, scope) if targetType == nil { return inner } return convertConstLocal(inner, targetType) case *SelectorExpr: pkgName, ok := e.X.(*Name) if !ok { return nil } var imported *TCPackage if scope != nil { _, obj := scope.LookupParent(pkgName.Value) if pn, ok2 := obj.(*PkgName); ok2 && pn.imported != nil { imported = pn.imported } } if imported == nil { ensureImportRegistry() imported = importRegistry[pkgName.Value] } if imported == nil { return nil } member := imported.Scope().Lookup(e.Sel.Value) if member == nil { return nil } if k, ok := member.(*TCConst); ok && k.val != nil { return k.val } return nil } return nil } func tcResolveNameInline(e Expr, scope *Scope) Type { if e == nil { return nil } switch e := e.(type) { case *Name: var obj Object if scope != nil { _, obj = scope.LookupParent(e.Value) } else { _, obj = Universe.LookupParent(e.Value) } if obj != nil { if tn, ok := obj.(*TypeName); ok { return tn.typ } } case *SelectorExpr: pkgName, ok := e.X.(*Name) if ok && scope != nil { _, pkgObj := scope.LookupParent(pkgName.Value) if pn, ok2 := pkgObj.(*PkgName); ok2 && pn.imported != nil { typeObj := pn.imported.scope.Lookup(e.Sel.Value) if typeObj != nil { if tn, ok3 := typeObj.(*TypeName); ok3 { return tn.typ } } } } // Fallback: check importRegistry directly for external package types if pkgName, ok := e.X.(*Name); ok { var irKeys []string for k := range importRegistry { irKeys = append(irKeys, k) } for i := 1; i < len(irKeys); i++ { for j := i; j > 0 && irKeys[j] < irKeys[j-1]; j-- { irKeys[j], irKeys[j-1] = irKeys[j-1], irKeys[j] } } for _, k := range irKeys { pkg := importRegistry[k] if pkg.Name() == pkgName.Value { typeObj := pkg.Scope().Lookup(e.Sel.Value) if typeObj != nil { if tn, ok2 := typeObj.(*TypeName); ok2 { return tn.typ } } break } } } case *Operation: if e.Y == nil && e.Op == Mul { base := tcResolveNameInline(e.X, scope) if base == nil { base = Typ[Int8] } return NewPointer(base) } case *SliceType: elem := tcResolveNameInline(e.Elem, scope) if elem != nil { if b, ok := elem.(*Basic); ok && b.kind == Uint8 { return Typ[TCString] } return NewSlice(elem) } case *ArrayType: elem := tcResolveNameInline(e.Elem, scope) if elem != nil { n := int64(-1) if lit, ok := e.Len.(*BasicLit); ok { n = irParseInt64(lit.Value) } else if e.Len != nil { cv := tcEvalConstExpr(e.Len, scope, -1) if cv != nil { if ci, ok := cv.(constInt); ok { n = ci.v } } if n == -1 && constValMap != nil { n = resolveArrayLenFromSrc(e.pos, constValMap) } } return NewArray(elem, n) } case *MapType: key := tcResolveNameInline(e.Key, scope) val := tcResolveNameInline(e.Value, scope) if key != nil && val != nil { return NewTCMap(key, val) } case *StructType: var fields []*TCVar var tags []string for i, field := range e.FieldList { typ := tcResolveNameInline(field.Type, scope) fname := "" if field.Name != nil { fname = field.Name.Value } else { fname = typeBaseName(typ) } fields = append(fields, NewTCField(nil, fname, typ, field.Name == nil)) tag := "" if i < len(e.TagList) && e.TagList[i] != nil { tag = e.TagList[i].Value } tags = append(tags, tag) } return NewTCStruct(fields, tags) case *FuncType: return tcResolveFuncInline(e, scope) case *InterfaceType: return tcResolveInterfaceInline(e, scope) case *DotsType: elem := tcResolveNameInline(e.Elem, scope) if elem != nil { if b, ok := elem.(*Basic); ok && b.kind == Uint8 { return Typ[TCString] } return NewSlice(elem) } } return nil } func tcResolveInterfaceInline(e *InterfaceType, scope *Scope) *TCInterface { var methods []*IfaceMethod for _, f := range e.MethodList { if f.Name == nil { continue } ft, ok := f.Type.(*FuncType) if !ok { continue } sig := tcResolveFuncInline(ft, scope) if sig != nil { methods = append(methods, NewTCIfaceMethod(f.Name.Value, sig)) } } iface := NewTCInterface(methods, nil) iface.Complete() return iface } func stripDuplicatePackageClauses(src []byte) []byte { found := false var out []byte i := 0 for i < len(src) { nlIdx := bytes.IndexByte(src[i:], '\n') var line []byte var lineEnd int if nlIdx < 0 { line = src[i:] lineEnd = len(src) } else { line = src[i : i+nlIdx] lineEnd = i + nlIdx + 1 } trimmed := bytes.TrimSpace(line) if bytes.HasPrefix(trimmed, "package ") { if found { if out == nil { out = []byte{:0:len(src)} out = append(out, src[:i]...) } for k := 0; k < len(line); k++ { out = append(out, ' ') } if nlIdx >= 0 { out = append(out, '\n') } i = lineEnd continue } found = true } if out != nil { out = append(out, src[i:lineEnd]...) } i = lineEnd } if out == nil { return src } return out } func rewriteSliceMakeLiterals(src []byte) []byte { var out []byte i := 0 for i < len(src) { start := bytes.Index(src[i:], []byte("{:")) if start < 0 { out = append(out, src[i:]...) break } start = start + i lbrack := findSliceTypeStart(src, start) if lbrack < 0 { out = append(out, src[i:start+2]...) i = start + 2 continue } close := findMatchingBrace(src, start) if close < 0 { out = append(out, src[i:start+2]...) i = start + 2 continue } inner := src[start+2 : close] colonIdx := bytes.IndexByte(inner, ':') typeText := src[lbrack:start] out = append(out, src[i:lbrack]...) if colonIdx < 0 { out = append(out, "make("...) out = append(out, typeText...) out = append(out, ", "...) out = append(out, bytes.TrimSpace(inner)...) out = append(out, ')') } else { lenExpr := bytes.TrimSpace(inner[:colonIdx]) capExpr := bytes.TrimSpace(inner[colonIdx+1:]) out = append(out, "make("...) out = append(out, typeText...) out = append(out, ", "...) out = append(out, lenExpr...) out = append(out, ", "...) out = append(out, capExpr...) out = append(out, ')') } i = close + 1 } if out == nil { return src } return out } func findSliceTypeStart(src []byte, braceIdx int) int { j := braceIdx - 1 for j >= 0 && (src[j] == ' ' || src[j] == '\t' || src[j] == '\n') { j-- } if j < 0 { return -1 } depth := 0 parenDepth := 0 candidate := -1 for j >= 0 { ch := src[j] if ch == ')' { parenDepth++ } else if ch == '(' { parenDepth-- } else if parenDepth > 0 { j-- continue } if ch == ']' { depth++ } else if ch == '[' { depth-- if depth == 0 { candidate = j } } else if depth == 0 && parenDepth == 0 { if candidate >= 0 { return candidate } if ch == ' ' || ch == '\t' || ch == '\n' || ch == '*' || ch == '(' || ch == ')' { j-- continue } if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '.' { j-- continue } return -1 } j-- } if candidate >= 0 { return candidate } return -1 } func findMatchingBrace(src []byte, openIdx int) int { depth := 1 for i := openIdx + 1; i < len(src); i++ { if src[i] == '{' { depth++ } else if src[i] == '}' { depth-- if depth == 0 { return i } } } return -1 } func rewriteChanMakeLiterals(src []byte) []byte { chanKw := []byte("chan ") var out []byte i := 0 for i < len(src) { idx := bytes.Index(src[i:], chanKw) if idx < 0 { if out != nil { out = append(out, src[i:]...) } break } idx = idx + i j := idx + 5 for j < len(src) && (src[j] == ' ' || src[j] == '\t') { j++ } if j >= len(src) { if out != nil { out = append(out, src[i:]...) } break } if src[j] == '<' && j+1 < len(src) && src[j+1] == '-' { if out != nil { out = append(out, src[i:j+2]...) } i = j + 2 continue } for j < len(src) && (src[j] != '{' && src[j] != '\n' && src[j] != ';' && src[j] != '(' && src[j] != ')') { if src[j] == ' ' || src[j] == '\t' { break } j++ } if j >= len(src) || src[j] != '{' { if out == nil { i = idx + 4 } else { out = append(out, src[i:idx+4]...) i = idx + 4 } continue } braceOpen := j close := findMatchingBrace(src, braceOpen) if close < 0 { if out == nil { i = idx + 4 } else { out = append(out, src[i:idx+4]...) i = idx + 4 } continue } inner := bytes.TrimSpace(src[braceOpen+1 : close]) chanType := src[idx : braceOpen] for len(chanType) > 0 && (chanType[len(chanType)-1] == ' ' || chanType[len(chanType)-1] == '\t') { chanType = chanType[:len(chanType)-1] } if out == nil { out = []byte{:0:len(src)} out = append(out, src[:idx]...) } else { out = append(out, src[i:idx]...) } if len(inner) == 0 { out = append(out, "make("...) out = append(out, chanType...) out = append(out, ')') } else { out = append(out, "make("...) out = append(out, chanType...) out = append(out, ", "...) out = append(out, inner...) out = append(out, ')') } i = close + 1 } if out == nil { return src } return out } func tcResolveRecvType(recv *Field, scope *Scope) Type { if recv == nil { return nil } return tcResolveNameInline(recv.Type, scope) } func tcResolveFuncInlineWithRecv(ft *FuncType, recv *Field, scope *Scope) *Signature { if ft == nil { return nil } var recvVar *TCVar if recv != nil { recvTyp := tcResolveNameInline(recv.Type, scope) recvName := "" if recv.Name != nil { recvName = recv.Name.Value } recvVar = NewTCVar(nil, recvName, recvTyp) } params := tcResolveFieldList(ft.ParamList, scope) results := tcResolveFieldList(ft.ResultList, scope) variadic := false if len(ft.ParamList) > 0 { if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*DotsType); ok { variadic = true } } return NewSignature(recvVar, params, results, variadic) } func tcResolveFieldList(fields []*Field, scope *Scope) *Tuple { if len(fields) == 0 { return nil } var vars []*TCVar for _, f := range fields { typ := tcResolveNameInline(f.Type, scope) pname := "" if f.Name != nil { pname = f.Name.Value } vars = append(vars, NewTCVar(nil, pname, typ)) } return NewTuple(vars...) } func tcResolveFuncInline(ft *FuncType, scope *Scope) *Signature { if ft == nil { return nil } var params []*TCVar for _, p := range ft.ParamList { typ := tcResolveNameInline(p.Type, scope) pname := "" if p.Name != nil { pname = p.Name.Value } params = append(params, NewTCVar(nil, pname, typ)) } var results []*TCVar for _, r := range ft.ResultList { typ := tcResolveNameInline(r.Type, scope) rname := "" if r.Name != nil { rname = r.Name.Value } results = append(results, NewTCVar(nil, rname, typ)) } variadic := false if len(ft.ParamList) > 0 { if _, ok := ft.ParamList[len(ft.ParamList)-1].Type.(*DotsType); ok { variadic = true } } var pTuple *Tuple if len(params) > 0 { pTuple = NewTuple(params...) } var rTuple *Tuple if len(results) > 0 { rTuple = NewTuple(results...) } return NewSignature(nil, pTuple, rTuple, variadic) } func (e *irEmitter) instrOperands(instr SSAInstruction) []SSAValue { switch i := instr.(type) { case *SSAStore: return []SSAValue{i.Addr, i.Val} case *SSAUnOp: return []SSAValue{i.X} case *SSABinOp: return []SSAValue{i.X, i.Y} case *SSACall: out := []SSAValue{i.Call.Value} for _, a := range i.Call.Args { out = append(out, a) } return out case *SSAFieldAddr: return []SSAValue{i.X} case *SSAIndexAddr: return []SSAValue{i.X, i.Index} case *SSAExtract: return []SSAValue{i.Tuple} case *SSAPhi: return i.Edges case *SSAReturn: var out []SSAValue for _, r := range i.Results { out = append(out, r) } return out case *SSAIf: return []SSAValue{i.Cond} case *SSAConvert: return []SSAValue{i.X} case *SSAChangeType: return []SSAValue{i.X} case *SSAMakeInterface: return []SSAValue{i.X} case *SSATypeAssert: return []SSAValue{i.X} case *SSASlice: out := []SSAValue{i.X} if i.Low != nil { out = append(out, i.Low) } if i.High != nil { out = append(out, i.High) } if i.Max != nil { out = append(out, i.Max) } return out case *SSAMapUpdate: return []SSAValue{i.Map, i.Key, i.Value} case *SSALookup: return []SSAValue{i.X, i.Index} case *SSARange: return []SSAValue{i.X} case *SSANext: return []SSAValue{i.Iter} case *SSASend: return []SSAValue{i.Chan, i.X} case *SSAMakeSlice: out := []SSAValue{i.Len} if i.Cap != nil { out = append(out, i.Cap) } if i.Data != nil { out = append(out, i.Data) } return out } return nil } func simpleItoa(n int) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } buf := []byte{:0:20} for n > 0 { buf = append(buf, byte('0'+n%10)) n /= 10 } if neg { buf = append(buf, '-') } for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 { buf[i], buf[j] = buf[j], buf[i] } return string(buf) }