package main import ( "bytes" "go/constant" "go/token" ) 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]bool allocTypes map[SSAValue]string hoisted map[SSAValue]bool } 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{}, } } 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 n, ok := e.valName[v]; ok { return n } name := v.SSAName() if name == "" { e.nextReg++ name = "r" | irItoa(e.nextReg) } n := "%" | name e.valName[v] = n return n } 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() 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 *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) 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 := "" if fn.Signature != nil && fn.Signature.Params() != nil { for i := 0; i < fn.Signature.Params().Len(); i++ { if i > 0 { params = params | ", " } params = params | e.llvmType(fn.Signature.Params().At(i).Type()) } } 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") 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) } } e.emitInitFunction() 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") } for name := range e.typeIDs { e.w("@") e.w(name) e.w(" = private constant i8 0\n") } if len(e.extDecls) > 0 { e.w("\n") for _, decl := range e.extDecls { e.w("declare ") e.w(decl) e.w("\n") } } if len(e.extGlobals) > 0 { e.w("\n") for name, typ := range e.extGlobals { e.w(name) e.w(" = external global ") e.w(typ) e.w("\n") } } return string(e.buf) } func (e *irEmitter) pkgMembersSorted() []SSAMember { 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] } } return members } func (e *irEmitter) emitGlobal(g *SSAGlobal) { name := e.globalName(g) typ := e.llvmType(g.typ) if p, ok := safeUnderlying(g.typ).(*Pointer); ok { typ = e.llvmType(p.Elem()) } 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 { 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) } } func (e *irEmitter) emitInitFunction() { pkgPath := e.pkg.Pkg.Path() hasInit := false for _, m := range e.pkg.Members { if fn, ok := m.(*SSAFunction); ok && fn.name == "init" { hasInit = true break } } if hasInit { return } e.w("\ndefine void @") e.w(pkgPath) e.w(".init(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 e.valName = map[SSAValue]string{} for i, p := range f.Params { pname := p.SSAName() if pname == "" { pname = "p" | irItoa(i) } e.valName[p] = "%" | pname } e.w("\ndefine ") 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(", ") } e.w(e.llvmType(p.SSAType())) e.w(" ") e.w(e.regName(p)) } 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 allocBlock := map[SSAValue]int{} 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 | "}" } } } 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 { 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 := allocBlock[ref]; ok && ab != 0 && ab != b.Index { hoistAllocs[ref] = true } } } } e.hoisted = hoistAllocs for _, b := range f.Blocks { if b.Index == 0 { e.w("entry:\n") for v := range hoistAllocs { if a, ok := v.(*SSAAlloc); ok { 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) { e.w("\ndeclare ") e.w(e.funcRetType(f)) e.w(" ") e.w(e.funcSymbol(f)) e.w("(") if f.Signature != nil && f.Signature.Params() != nil { for i := 0; i < f.Signature.Params().Len(); i++ { if i > 0 { e.w(", ") } e.w(e.llvmType(f.Signature.Params().At(i).Type())) } if f.Signature.Params().Len() > 0 { e.w(", ") } } e.w("ptr") e.w(")\n") } 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.llvmType(f.Signature.Results().At(0).Type()) } s := "{" for i := 0; i < f.Signature.Results().Len(); i++ { if i > 0 { s = s | ", " } s = s | e.llvmType(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") if b.Index == 0 && len(e.curFunc.FreeVars) > 0 { e.emitFreeVarUnpack(e.curFunc) } for _, instr := range b.Instrs { e.emitInstr(instr) } } 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) 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 } } 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' if !bothScalar { elemType = override 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") } } func (e *irEmitter) inferAllocTypeFromStores(a *SSAAlloc) string { for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if s, ok := instr.(*SSAStore); ok && s.Addr == a { // Check allocTypes override first 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 } // Check if stored value is an append builtin call (result is always a slice) if call, ok := s.Val.(*SSACall); ok { if b, ok2 := call.Call.Value.(*SSABuiltin); ok2 && b.SSAName() == "append" { return e.sliceType() } } } } } return "ptr" } func (e *irEmitter) emitStore(s *SSAStore) { valType := e.llvmType(s.Val.SSAType()) val := e.operand(s.Val) 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 value type is still generic ptr but the address has a known alloc type, use that if valType == "ptr" || valType == "void" { if at, ok := e.allocTypes[s.Addr]; ok && at != "ptr" && at != "void" { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } if valType == "void" { 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) 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 { 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 { 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()) { isActuallyIface := false if at, ok := e.allocTypes[b.X]; ok && at == e.ifaceType() { isActuallyIface = true } if !isActuallyIface { 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) if !isIface && !isSlice && !isSig && !isPtr && u == nil && (lt == "{ptr, ptr}" || lt == "{ptr, i64}") { isIface = true } if isIface || isSlice || isSig || 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 { e.emitStructCompare(reg, b.Op, st, 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 { 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(", 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(", ") ; 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(", 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(lv) ; e.w(", ") ; e.w(extReg) ; 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 { 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()) if lt != rt && e.intBits(lt) > 0 && e.intBits(rt) > 0 { resType := e.llvmType(b.SSAType()) if 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 } } op := e.llvmBinOp(b.Op, b.X.SSAType()) if op == "" { e.w(" ; unsupported binop\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 (e *irEmitter) emitSliceConcat(reg string, sl *Slice, lv, rv string) { ipt := e.intptrType() sty := "{ptr, " | ipt | ", " | ipt | "}" elemType := e.llvmType(sl.Elem()) p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } xPtr := p("cc") e.w(" ") ; e.w(xPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 0\n") xLen := p("cc") e.w(" ") ; e.w(xLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(lv) ; e.w(", 1\n") yPtr := p("cc") e.w(" ") ; e.w(yPtr) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 0\n") yLen := p("cc") e.w(" ") ; e.w(yLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(rv) ; e.w(", 1\n") elemSz := p("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 := p("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 := p("cc") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := p("cc") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := p("cc") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := p("cc") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := p("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 (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" } 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: p := func() string { e.nextReg++ return "%sc" | irItoa(e.nextReg) } tmp := p() 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: p := func() string { e.nextReg++ return "%sc" | irItoa(e.nextReg) } tmp := p() 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: p := func() string { e.nextReg++ return "%sc" | irItoa(e.nextReg) } tmp := p() 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) { p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } 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 := p("sf") rf := p("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 := p("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 := p("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) 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: return "and" case OpOr: return "or" case OpLand: return "and" case 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 } else 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' if !bothScalar { 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") return } valType := e.llvmType(u.X.SSAType()) val := e.operand(u.X) switch u.Op { case OpSub: isFloat := false if b, ok := safeUnderlying(u.X.SSAType()).(*Basic); ok { isFloat = b.Info()&IsFloat != 0 } e.w(" ") e.w(reg) if isFloat { e.w(" = fneg ") } else { e.w(" = sub ") e.w(valType) e.w(" 0, ") val = val e.w(val) e.w("\n") return } e.w(valType) e.w(" ") e.w(val) e.w("\n") case OpNot: e.w(" ") e.w(reg) e.w(" = xor ") e.w(valType) e.w(" ") e.w(val) e.w(", -1\n") case OpXor: e.w(" ") e.w(reg) e.w(" = xor ") e.w(valType) e.w(" ") e.w(val) e.w(", -1\n") default: e.w(" ; unsupported unop\n") } } func (e *irEmitter) callArgType(arg SSAValue, sig *Signature, i int) string { if at, ok := e.allocTypes[arg]; ok { return at } t := e.llvmType(arg.SSAType()) if t != "void" { return t } if sig != nil && sig.Params() != nil && i < sig.Params().Len() { return e.llvmType(sig.Params().At(i).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(", ") } 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 null") e.w(")\n") return } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } funcVal := e.operand(c.Call.Value) funcPtr := p("fp") ctx := p("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() ipt := e.intptrType() sty := e.sliceType() switch name { case "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 } _, 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 } } case "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 } _, 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 } } case "append": if len(c.Call.Args) > 2 { src := e.operand(c.Call.Args[0]) elemType := "i8" if sl, ok := safeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } if elemType == "i8" || elemType == "void" { for j := 1; j < len(c.Call.Args); j++ { et := e.llvmType(c.Call.Args[j].SSAType()) if et != "void" && et != "" && et != "i8" { elemType = et break } } } nElems := len(c.Call.Args) - 1 p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } arrAlloca := p("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]) gep := p("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 := p("ap") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := p("ap") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") srcCap := p("ap") e.w(" ") ; e.w(srcCap) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 2\n") elemSz := p("ap") 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 := p("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(ipt) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(srcCap) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(irItoa(nElems)) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := p("ap") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := p("ap") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := p("ap") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := p("ap") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := p("ap") 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) return } if len(c.Call.Args) == 2 { src := e.operand(c.Call.Args[0]) elems := e.operand(c.Call.Args[1]) elemType := "i8" if sl, ok := safeUnderlying(c.Call.Args[0].SSAType()).(*Slice); ok { elemType = e.llvmType(sl.Elem()) } if elemType == "i8" || elemType == "void" { et := e.llvmType(c.Call.Args[1].SSAType()) if et != "void" && et != "" && et != "i8" { elemType = et } } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } srcBuf := p("ap") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := p("ap") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") srcCap := p("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 := safeUnderlying(c.Call.Args[1].SSAType()).(*Slice) if !arg1IsSlice { if b, ok := safeUnderlying(c.Call.Args[1].SSAType()).(*Basic); ok && b.Info()&IsString != 0 { arg1IsSlice = true } } if arg1IsSlice { elemsBuf = p("ap") e.w(" ") ; e.w(elemsBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 0\n") elemsLen = p("ap") e.w(" ") ; e.w(elemsLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(elems) ; e.w(", 1\n") } else { alloca := p("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 := p("ap") 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 := p("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(ipt) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(srcCap) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemsLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemSz) e.w(")\n") newPtr := p("ap") e.w(" ") ; e.w(newPtr) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 0\n") newLen := p("ap") e.w(" ") ; e.w(newLen) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 1\n") newCap := p("ap") e.w(" ") ; e.w(newCap) ; e.w(" = extractvalue ") ; e.w(retTy) ; e.w(" ") ; e.w(result) ; e.w(", 2\n") s1 := p("ap") e.w(" ") ; e.w(s1) ; e.w(" = insertvalue ") ; e.w(sty) ; e.w(" undef, ptr ") ; e.w(newPtr) ; e.w(", 0\n") s2 := p("ap") 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) return } case "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()) } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } dstBuf := p("cp") e.w(" ") ; e.w(dstBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 0\n") dstLen := p("cp") e.w(" ") ; e.w(dstLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(dst) ; e.w(", 1\n") srcBuf := p("cp") e.w(" ") ; e.w(srcBuf) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 0\n") srcLen := p("cp") e.w(" ") ; e.w(srcLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(src) ; e.w(", 1\n") elemSz := p("cp") 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") callReg := p("cp") e.w(" ") ; e.w(callReg) e.w(" = call ") ; e.w(ipt) ; e.w(" @runtime.sliceCopy(ptr ") e.w(dstBuf) ; e.w(", ptr ") ; e.w(srcBuf) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(dstLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(srcLen) e.w(", ") ; e.w(ipt) ; e.w(" ") ; e.w(elemSz) e.w(")\n") retType := e.llvmType(c.SSAType()) if retType != ipt { e.w(" ") ; e.w(reg) ; e.w(" = trunc ") ; e.w(ipt) ; 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(ipt) ; e.w(" ") ; e.w(callReg) ; e.w(", 0\n") } e.declareRuntime("runtime.sliceCopy", ipt, "ptr, ptr, " | ipt | ", " | ipt | ", " | ipt) return } case "print", "println": e.w(" call void @runtime.printlock()\n") for i, arg := range c.Call.Args { if i > 0 && name == "println" { e.w(" call void @runtime.printspace()\n") } av := e.operand(arg) at := arg.SSAType() e.emitPrintArg(av, at) } if name == "println" { 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 name == "println" && len(c.Call.Args) > 1 { e.declareRuntime("runtime.printspace", "void", "") } return case "delete": if len(c.Call.Args) == 2 { mapVal := e.operand(c.Call.Args[0]) keyVal := e.operand(c.Call.Args[1]) mt, _ := safeUnderlying(c.Call.Args[0].SSAType()).(*TCMap) keyType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } keyAlloca := p("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 } case "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") return } case "min", "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 name == "max" { cmpOp = "sgt" } u := safeUnderlying(c.SSAType()) if bb, ok := u.(*Basic); ok && bb.Info()&IsUnsigned != 0 { cmpOp = "ult" if name == "max" { 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 name == "recover" { retType = e.ifaceType() } if retType != "void" && retType != "" { if retType == "ptr" || e.intBits(retType) > 0 || retType == "i1" { e.emitZeroReg(reg, c.SSAType()) } else { // Aggregate type: use alloca to get zeroinitializer 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() for i, edge := range p.Edges { if i > 0 { e.w(", ") } e.w("[") e.w(e.operand(edge)) e.w(", ") if i < len(blk.Preds) { e.w(e.blockLabel(blk.Preds[i])) } else { e.w("%unknown") } e.w("]") } e.w("\n") } 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 } 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) emitReturn(r *SSAReturn) { if len(r.Results) == 0 { rt := e.funcRetType(e.curFunc) if rt == "void" { e.w(" ret void\n") } else { e.w(" unreachable\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 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++ r := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = inttoptr ") ; e.w(typ) ; e.w(" ") ; e.w(val) ; e.w(" to ptr\n") val = r typ = "ptr" } else if typ == "ptr" && e.intBits(expectType) > 0 { e.nextReg++ r := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(expectType) ; e.w("\n") val = r 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.llvmType(sig.Results().At(i).Type())) } } retType := "{" for i, res := range r.Results { 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, res := range r.Results { 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 { 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 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() 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) 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 == "void" || c.X.SSAType() == nil { if dstType == "ptr" { e.valName[c] = "null" } else { e.valName[c] = "zeroinitializer" } return } if srcType == dstType { e.valName[c] = e.operand(c.X) return } op := e.conversionOp(c.X.SSAType(), c.SSAType()) if op == "ptrtoint" && e.intBits(dstType) == 0 { if dstType == e.ifaceType() { e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} zeroinitializer, 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 && !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 X is a load (struct by value), use the load's source alloca instead if uop, ok := f.X.(*SSAUnOp); ok { addrType := e.llvmType(uop.X.SSAType()) useSource := false if p, ok2 := safeUnderlying(uop.X.SSAType()).(*Pointer); ok2 && p.Elem() != nil { elem := p.Elem() if p2, ok3 := safeUnderlying(elem).(*Pointer); ok3 && p2.Elem() != nil { // **Struct: load indirection, don't replace base baseType = e.llvmType(p2.Elem()) } else { // *Struct: source is struct storage, safe to GEP directly baseType = e.llvmType(elem) useSource = true } } if useSource && 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) _, isSlice := safeUnderlying(idx.X.SSAType()).(*Slice) if !isSlice { if b, ok := safeUnderlying(idx.X.SSAType()).(*Basic); ok && b.Info()&IsString != 0 { isSlice = true } } 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 } _, isArray := safeUnderlying(idx.X.SSAType()).(*Array) if isArray { e.nextReg++ arrPtr := "%ai" | irItoa(e.nextReg) arrType := e.llvmType(idx.X.SSAType()) 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") 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 } 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) e.w(" ") ; e.w(ext) ; e.w(" = sext ") ; 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 if arr, ok := safeUnderlying(s.X.SSAType()).(*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 { 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") } toIpt := func(val SSAValue, operandStr string) string { if val == nil { return operandStr } valType := e.llvmType(val.SSAType()) if valType == ipt { return operandStr } e.nextReg++ ext := "%sl" | irItoa(e.nextReg) e.w(" ") ; e.w(ext) ; e.w(" = sext ") ; e.w(valType) ; e.w(" ") ; e.w(operandStr) ; e.w(" to ") ; e.w(ipt) ; e.w("\n") return ext } low := "0" if s.Low != nil { low = toIpt(s.Low, e.operand(s.Low)) } high := oldLen if s.High != nil { high = toIpt(s.High, e.operand(s.High)) } maxCap := oldCap if s.Max != nil { maxCap = toIpt(s.Max, e.operand(s.Max)) } 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()) } 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) emitMakeInterface(m *SSAMakeInterface) { reg := e.regName(m) val := e.operand(m.X) valType := e.llvmType(m.X.SSAType()) if at, ok := e.allocTypes[m.X]; ok && at != "ptr" && at != "void" { valType = at } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } if valType == e.ifaceType() { tp := p("mi") e.w(" ") ; e.w(tp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 0\n") dp := p("mi") e.w(" ") ; e.w(dp) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") t1 := p("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 { valPtr = p("mi") e.w(" ") ; e.w(valPtr) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") e.w(" store ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") } typeid := e.typeIDGlobal(m.X.SSAType()) t1 := p("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.typeIDName(t) if e.typeIDs == nil { e.typeIDs = map[string]bool{} } if !e.typeIDs[name] { e.typeIDs[name] = true } return "@" | name } func (e *irEmitter) typeIDName(t Type) string { pkg := e.pkg.Pkg.Path() if named, ok := t.(*Named); ok && named.Obj() != nil { return pkg | ".typeid." | named.Obj().Name() } if p, ok := t.(*Pointer); ok { if named, ok := p.Elem().(*Named); ok && named.Obj() != nil { return pkg | ".typeid.ptr." | named.Obj().Name() } } raw := e.llvmType(t) safe := []byte{:len(raw)} safe = safe[:0] for i := 0; i < len(raw); i++ { c := raw[i] if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c == '.' { safe = append(safe, c) } else if c == '{' || c == '}' || c == ',' || c == ' ' || c == '*' { safe = append(safe, '_') } } return pkg | ".typeid." | string(safe) } func (e *irEmitter) findIfaceImpls(methodName string) []ifaceImpl { var impls []ifaceImpl for mname, m := range e.pkg.Members { 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] obj := e.pkg.Pkg.Scope().Lookup(tname) if obj == nil { continue } tn, ok2 := obj.(*TypeName) if !ok2 || tn.Type() == nil { continue } isPtrRecv := fn.object != nil && fn.object.HasPtrRecv() impls = append(impls, ifaceImpl{fn: fn, recvType: tn.Type(), ptrRecv: isPtrRecv}) } return impls } type ifaceImpl struct { fn *SSAFunction recvType Type ptrRecv bool } func (e *irEmitter) emitInvoke(inv *SSAInvoke) { reg := e.regName(inv) ifaceVal := e.operand(inv.X) retType := e.llvmType(inv.SSAType()) isVoid := retType == "void" p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } tidPtr := p("tid") e.w(" ") ; e.w(tidPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(ifaceVal) ; e.w(", 0\n") valPtr := p("vp") e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(ifaceVal) ; e.w(", 1\n") impls := e.findIfaceImpls(inv.MethodName) 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] var recvLLVM, recv string if impl.ptrRecv { recvLLVM = "ptr" recv = valPtr } else { recvLLVM = e.llvmType(impl.recvType) if recvLLVM == "ptr" { recv = valPtr } else { recv = p("ld") e.w(" ") ; e.w(recv) ; e.w(" = load ") ; e.w(recvLLVM) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") } } e.w(" ") if !isVoid { e.w(reg) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.funcSymbol(impl.fn)) ; e.w("(") e.w(recvLLVM) ; e.w(" ") ; e.w(recv) for i, arg := range inv.Args { argT := e.llvmType(arg.SSAType()) if argT == "void" { argT = "ptr" } e.w(", ") ; e.w(argT) ; e.w(" ") ; e.w(e.operand(arg)) _ = i } 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, p("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.typeIDGlobal(impl.recvType) cmpReg := p("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 if impl.ptrRecv { recvLLVM = "ptr" recv = valPtr } else { recvLLVM = e.llvmType(impl.recvType) if recvLLVM == "ptr" { recv = valPtr } else { recv = p("ld") e.w(" ") ; e.w(recv) ; e.w(" = load ") ; e.w(recvLLVM) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") } } e.w(" ") if !isVoid { e.w(callRegs[i]) ; e.w(" = ") } e.w("call ") ; e.w(retType) ; e.w(" ") ; e.w(e.funcSymbol(impl.fn)) ; e.w("(") e.w(recvLLVM) ; e.w(" ") ; e.w(recv) for ii, arg := range inv.Args { argT := e.llvmType(arg.SSAType()) if argT == "void" { argT = "ptr" } e.w(", ") ; e.w(argT) ; e.w(" ") ; e.w(e.operand(arg)) _ = ii } 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 !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" } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } // 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 = p("ta") e.w(" ") ; e.w(valPtr) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") typePtr = p("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 := p("ta") e.w(" ") ; e.w(ok) ; e.w(" = icmp eq ptr ") ; e.w(typePtr) ; e.w(", ") ; e.w(tidGlobal) ; e.w("\n") loaded := p("ta") e.w(" ") ; e.w(loaded) ; e.w(" = load ") ; e.w(assertedType) ; e.w(", ptr ") ; e.w(valPtr) ; e.w("\n") tupType := "{" | assertedType | ", i1}" t1 := p("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 { 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) emitMakeMap(m *SSAMakeMap) { reg := e.regName(m) ipt := e.intptrType() mt, _ := safeUnderlying(m.SSAType()).(*TCMap) 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" } } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } keySz := p("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 := p("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() } mt, _ := safeUnderlying(mapType).(*TCMap) keyType := "i32" valType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) valType = e.llvmType(mt.Elem()) } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } if keyVal == "null" && keyType != "ptr" { keyVal = "zeroinitializer" } if valVal == "null" && valType != "ptr" { valVal = "zeroinitializer" } keyAlloca := p("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 := p("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()) { e.w(" call void @runtime.hashmapContentSet(ptr ") ; e.w(mapVal) e.w(", ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) e.w(", ptr ") ; e.w(valAlloca) ; e.w(")\n") e.declareRuntime("runtime.hashmapContentSet", "void", "ptr, " | keyType | ", 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) mt, _ := safeUnderlying(l.X.SSAType()).(*TCMap) keyType := "i32" valType := "i32" if mt != nil { keyType = e.llvmType(mt.Key()) valType = e.llvmType(mt.Elem()) } p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } valAlloca := p("ml") e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") valSz := p("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()) { okReg := p("ml") e.w(" ") ; e.w(okReg) ; e.w(" = call i1 @runtime.hashmapContentGet(ptr ") ; e.w(mapVal) e.w(", ") ; e.w(keyType) ; e.w(" ") ; e.w(keyVal) 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, " | keyType | ", ptr, " | ipt) if l.CommaOk { loaded := p("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 := p("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 := p("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 := p("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 := p("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 := p("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) fn, _ := m.Fn.(*SSAFunction) p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } ipt := e.intptrType() if len(m.Bindings) == 0 { t1 := p("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 := p("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 := p("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) btype := e.llvmType(b.SSAType()) gep := p("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(btype) ; e.w(" ") ; e.w(bval) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") } t1 := p("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) closureContextType(bindings []SSAValue) string { s := "{" for i, b := range bindings { if i > 0 { s = s | ", " } s = s | e.llvmType(b.SSAType()) } return s | "}" } func (e *irEmitter) emitFreeVarUnpack(f *SSAFunction) { ctxTypes := []string{:len(f.FreeVars)} for i, fv := range f.FreeVars { ctxTypes[i] = e.llvmType(fv.SSAType()) } ctxType := "{" for i, t := range ctxTypes { if i > 0 { ctxType = ctxType | ", " } ctxType = ctxType | t } 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 ") ; e.w(ctxTypes[i]) e.w(", ptr ") ; e.w(gep) ; e.w("\n") } } func (e *irEmitter) emitPanic(p *SSAPanic) { e.w(" call void @runtime._panic(ptr null)\n") e.w(" unreachable\n") e.declareRuntime("runtime._panic", "void", "ptr") } 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) p := func(prefix string) string { e.nextReg++ return "%" | prefix | irItoa(e.nextReg) } if mt, ok := safeUnderlying(rangeInstr.X.SSAType()).(*TCMap); ok { e.emitNextMap(reg, iterPtr, collVal, mt, n, p) return } if arr, ok := safeUnderlying(rangeInstr.X.SSAType()).(*Array); ok { e.emitNextArray(reg, iterPtr, collVal, arr, n, p) 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, p) } func (e *irEmitter) emitNextSlice(reg, iterPtr, collVal string, rangeInstr *SSARange, n *SSANext, p func(string) string) { ipt := e.intptrType() sty := e.sliceType() idx := p("rn") e.w(" ") ; e.w(idx) ; e.w(" = load ") ; e.w(ipt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") slLen := p("rn") e.w(" ") ; e.w(slLen) ; e.w(" = extractvalue ") ; e.w(sty) ; e.w(" ") ; e.w(collVal) ; e.w(", 1\n") ok := p("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 := p("rn") e.w(" ") ; e.w(key) ; e.w(" = trunc ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(" to i32\n") dataPtr := p("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 := p("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 := p("rn") e.w(" ") ; e.w(fallback) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") safePtr := p("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 := p("rn") e.w(" ") ; e.w(elem) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(safePtr) ; e.w("\n") inc := p("rn") e.w(" ") ; e.w(inc) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", 1\n") newCnt := p("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 := e.llvmType(n.SSAType()) t1 := p("rn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := p("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, p func(string) string) { ipt := e.intptrType() arrLen := arr.Len() elemType := e.llvmType(arr.Elem()) arrType := "[" | irItoa(int(arrLen)) | " x " | elemType | "]" idx := p("rn") e.w(" ") ; e.w(idx) ; e.w(" = load ") ; e.w(ipt) ; e.w(", ptr ") ; e.w(iterPtr) ; e.w("\n") ok := p("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 := p("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 := p("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 := p("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 := p("rn") e.w(" ") ; e.w(fallback) ; e.w(" = alloca ") ; e.w(elemType) ; e.w("\n") safePtr := p("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 := p("rn") e.w(" ") ; e.w(elem) ; e.w(" = load ") ; e.w(elemType) ; e.w(", ptr ") ; e.w(safePtr) ; e.w("\n") inc := p("rn") e.w(" ") ; e.w(inc) ; e.w(" = add ") ; e.w(ipt) ; e.w(" ") ; e.w(idx) ; e.w(", 1\n") newCnt := p("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 := p("rn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := p("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, p func(string) string) { keyType := e.llvmType(mt.Key()) valType := e.llvmType(mt.Elem()) keyAlloca := p("mn") e.w(" ") ; e.w(keyAlloca) ; e.w(" = alloca ") ; e.w(keyType) ; e.w("\n") valAlloca := p("mn") e.w(" ") ; e.w(valAlloca) ; e.w(" = alloca ") ; e.w(valType) ; e.w("\n") ok := p("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 := p("mn") e.w(" ") ; e.w(key) ; e.w(" = load ") ; e.w(keyType) ; e.w(", ptr ") ; e.w(keyAlloca) ; e.w("\n") val := p("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 := p("mn") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue ") ; e.w(tupType) ; e.w(" undef, i1 ") ; e.w(ok) ; e.w(", 0\n") t2 := p("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) 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: s := c.val.String() if s == "true" { return "true" } return "false" case Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64, UntypedInt, UntypedRune: return c.val.String() case Float32, Float64, UntypedFloat: return c.val.String() case TCString, UntypedString: cv, ok := c.val.(constant.Value) if !ok { return "zeroinitializer" } s := constant.StringVal(cv) if len(s) == 0 { return "zeroinitializer" } idx := e.addStringConst(s) ipt := e.intptrType() slen := irItoa64(int64(len(s))) return "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }" } } 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 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 CompileToIR(src []byte, name string, triple string) string { initUniverse() prog := NewSSAProgram() pkg := newTCPackageWithUniverse(name, name) scope := pkg.Scope() src = rewriteSliceMakeLiterals(src) src = stripDuplicatePackageClauses(src) var parseErrors []string errh := func(err error) { parseErrors = append(parseErrors, err.Error()) } r := bytes.NewReader(src) file, err := Parse(NewFileBase(name|".mx"), r, errh, nil, 0) if err != nil || file == nil { msg := "; parse error" for _, e := range parseErrors { msg = msg | ": " | e } return msg | "\n" } 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)) } } } 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 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) } } } } } 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" { 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 } } } case *ConstDecl: if d.Group == nil || d.Group != curConstGroup { curConstGroup = d.Group iotaVal = 0 prevConstValues = nil prevConstType = nil } else { iotaVal++ } valExpr := d.Values typeExpr := d.Type if valExpr == nil { valExpr = prevConstValues } if typeExpr == nil && d.Type == nil { typeExpr = prevConstType } if d.Values != nil { prevConstValues = d.Values } if d.Type != nil { prevConstType = d.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 = Typ[UntypedInt] } for _, n := range d.NameList { obj := scope.Lookup(n.Value) if obj != nil { if c, ok := obj.(*TCConst); ok { c.typ = typ c.val = val } } } } } 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 { named, _ = pt.Elem().(*Named) isPtr = true } else { named, _ = recvType.(*Named) } if named != nil { if isPtr { fn.hasPtrRecv = true } named.AddMethod(fn) } } } ssaPkg := prog.CreatePackage(pkg, []*File{file}, nil) emitter := newIREmitter(ssaPkg, triple) return emitter.emit() } 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 } func tcEvalConstExpr(e Expr, scope *Scope, iotaVal int64) ConstVal { if e == nil { return nil } switch e := e.(type) { case *BasicLit: return evalBasicLit(e) case *Name: if e.Value == "iota" && iotaVal >= 0 { return constant.MakeInt64(iotaVal) } if scope != nil { _, obj := scope.LookupParent(e.Value) if c, ok := obj.(*TCConst); ok && c.val != nil { return c.val } } case *Operation: if e.Y == nil { return tcEvalConstExpr(e.X, scope, iotaVal) } xr := tcEvalConstExpr(e.X, scope, iotaVal) yr := tcEvalConstExpr(e.Y, scope, iotaVal) if xr == nil || yr == nil { return nil } xv, _ := xr.(constant.Value) yv, _ := yr.(constant.Value) if xv == nil || yv == nil { return nil } switch e.Op { case Add: return constant.BinaryOp(xv, token.ADD, yv) case Sub: return constant.BinaryOp(xv, token.SUB, yv) case Mul: return constant.BinaryOp(xv, token.MUL, yv) case Div: return constant.BinaryOp(xv, token.QUO, yv) case Shl: shift, _ := constant.Uint64Val(yv) return constant.Shift(xv, token.SHL, uint(shift)) case Shr: shift, _ := constant.Uint64Val(yv) return constant.Shift(xv, token.SHR, uint(shift)) case Or: return constant.BinaryOp(xv, token.OR, yv) case And: return constant.BinaryOp(xv, token.AND, yv) case Xor: return constant.BinaryOp(xv, token.XOR, yv) } case *ParenExpr: return tcEvalConstExpr(e.X, scope, iotaVal) } 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 { for _, pkg := range importRegistry { 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 gv, ok := cv.(constant.Value); ok { if iv, ok2 := constant.Int64Val(gv); ok2 { n = iv } } } } 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 } 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 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 { return j } } else if depth == 0 && parenDepth == 0 { 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-- } 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 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 }