package emit import ( "git.smesh.lol/moxie/pkg/syntax" "git.smesh.lol/moxie/pkg/ssa" "git.smesh.lol/moxie/pkg/token" "git.smesh.lol/moxie/pkg/types" ) func (e *irEmitter) emitInstr(instr ssa.SSAInstruction) { switch i := instr.(type) { case *ssa.SSAAlloc: if e.hoisted != nil && e.hoisted[i] { break } e.emitAlloc(i) case *ssa.SSAStore: e.emitStore(i) case *ssa.SSABinOp: e.emitBinOp(i) case *ssa.SSAUnOp: e.emitUnOp(i) case *ssa.SSACall: e.emitCall(i) case *ssa.SSAPhi: e.emitPhi(i) case *ssa.SSAReturn: e.emitReturn(i) case *ssa.SSAJump: e.emitJump(i) case *ssa.SSAIf: e.emitIf(i) case *ssa.SSAConvert: e.emitConvert(i) case *ssa.SSAChangeType: e.emitChangeType(i) case *ssa.SSAFieldAddr: e.emitFieldAddr(i) case *ssa.SSAIndexAddr: e.emitIndexAddr(i) case *ssa.SSAExtract: e.emitExtract(i) case *ssa.SSAMakeSlice: e.emitMakeSlice(i) case *ssa.SSASlice: e.emitSliceOp(i) case *ssa.SSAMakeInterface: e.emitMakeInterface(i) case *ssa.SSAInvoke: e.emitInvoke(i) case *ssa.SSATypeAssert: e.emitTypeAssert(i) case *ssa.SSAMakeMap: e.emitMakeMap(i) case *ssa.SSAMapUpdate: e.emitMapUpdate(i) case *ssa.SSALookup: e.emitLookup(i) case *ssa.SSAMakeClosure: e.emitMakeClosure(i) case *ssa.SSAPanic: e.emitPanic(i) case *ssa.SSARunDefers: e.emitRunDefers() case *ssa.SSADefer: e.emitDefer(i) case *ssa.SSASend: e.emitChanSend(i) case *ssa.SSAGo: e.w(" ; go\n") case *ssa.SSASelect: e.w(" ; select\n") case *ssa.SSARange: e.emitRange(i) case *ssa.SSANext: e.emitNext(i) case *ssa.SSAMakeChan: e.emitMakeChan(i) } } func (e *irEmitter) emitAlloc(a *ssa.SSAAlloc) { reg := e.regName(a) if at, ok := e.allocTypes[a]; ok && len(at) > 0 && at[0] == '[' { if a.Heap { ipt := e.intptrType() e.nextReg++ sz := "%ha" | irItoa(e.nextReg) e.w(" ") ; e.w(sz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(at) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") e.w(" ") ; e.w(reg) e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") e.deallocAfterAlloc(a, reg) e.scopeTrackAlloc(reg) } else { e.w(" ") ; e.w(reg) ; e.w(" = alloca ") ; e.w(at) ; e.w("\n") e.emitZeroInit(at, reg) } return } elemType := e.llvmType(a.SSAType()) nilElem := false if p, ok := types.SafeUnderlying(a.SSAType()).(*types.Pointer); ok { if p.Elem() != nil { elemType = e.llvmType(p.Elem()) } else { nilElem = true } } isDoublePtr := false if p, ok := types.SafeUnderlying(a.SSAType()).(*types.Pointer); ok && p.Elem() != nil { if _, ok2 := types.SafeUnderlying(p.Elem()).(*types.Pointer); ok2 { isDoublePtr = true } } if isDoublePtr && elemType == "ptr" { e.allocTypes[a] = elemType } else if elemType == "void" || (elemType == "ptr" && nilElem) { inferred := e.inferAllocTypeFromStores(a) if inferred != "ptr" || elemType == "void" { elemType = inferred } e.allocTypes[a] = elemType } else { override := e.inferAllocTypeFromStores(a) if override != "ptr" && override != elemType { bothScalar := len(elemType) > 0 && elemType[0] == 'i' && len(override) > 0 && override[0] == 'i' isFloatToInt := (elemType == "double" || elemType == "float") && len(override) > 0 && override[0] == 'i' isScalarToAgg := len(elemType) > 0 && (elemType[0] == 'i' || elemType == "double" || elemType == "float") && len(override) > 0 && override[0] == '{' isAggToScalar := len(elemType) > 0 && elemType[0] == '{' && len(override) > 0 && override[0] == 'i' if !bothScalar && !isFloatToInt && !isScalarToAgg && !isAggToScalar { elemType = override e.allocTypes[a] = elemType } } if elemType == "i32" { ssaIsInt := false if p, ok2 := types.SafeUnderlying(a.SSAType()).(*types.Pointer); ok2 && p.Elem() != nil { if b2, ok3 := types.SafeUnderlying(p.Elem()).(*types.Basic); ok3 && b2.Info()&types.IsInteger != 0 { ssaIsInt = true } } if !ssaIsInt { usage := e.inferAllocTypeFromUsage(a) if usage != "" && usage != "i32" && usage != "void" && usage != "ptr" { elemType = usage e.allocTypes[a] = elemType } } } } if !isDoublePtr { if faType := e.inferAllocTypeFromFieldAddrs(a, elemType); faType != "" { retType := e.inferAllocTypeFromReturn(a) callType := e.inferAllocTypeFromCallArgs(a) appendType := e.inferAllocTypeFromAppendUsage(a) best := faType if retType != "" && len(retType) > len(best) { best = retType } if callType != "" && len(callType) > len(best) { best = callType } if appendType != "" && len(appendType) > len(best) { best = appendType } if elemType != best { elemType = best e.allocTypes[a] = elemType } } } if a.Heap { ipt := e.intptrType() e.nextReg++ sz := "%ha" | irItoa(e.nextReg) e.w(" ") ; e.w(sz) e.w(" = ptrtoint ptr getelementptr (") ; e.w(elemType) e.w(", ptr null, i32 1) to ") ; e.w(ipt) ; e.w("\n") e.w(" ") ; e.w(reg) e.w(" = call ptr @runtime.alloc(") ; e.w(ipt) e.w(" ") ; e.w(sz) ; e.w(", ptr null, ptr null)\n") e.declareRuntime("runtime.alloc", "ptr", ipt | ", ptr, ptr") e.deallocAfterAlloc(a, reg) e.scopeTrackAlloc(reg) } else { e.w(" ") e.w(reg) e.w(" = alloca ") e.w(elemType) e.w("\n") e.emitZeroInit(elemType, reg) } } func (e *irEmitter) inferAllocTypeFromStores(a *ssa.SSAAlloc) string { allocName := a.SSAName() for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if s, ok := instr.(*ssa.SSAStore); ok && s.Addr != nil && s.Addr.SSAName() == allocName { if at, ok2 := e.allocTypes[s.Val]; ok2 && at != "ptr" && at != "void" { return at } vt := e.llvmType(s.Val.SSAType()) if vt != "void" && vt != "" { return vt } if call, ok := s.Val.(*ssa.SSACall); ok { if b2, ok2 := call.Call.Value.(*ssa.SSABuiltin); ok2 && b2.SSAName() == "append" { return e.sliceType() } } if _, ok := s.Val.(*ssa.SSASlice); ok { return e.sliceType() } if _, ok := s.Val.(*ssa.SSAMakeSlice); ok { return e.sliceType() } } } } return "ptr" } func (e *irEmitter) inferAllocTypeFromReturn(a *ssa.SSAAlloc) string { allocName := a.SSAName() for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { ret, ok := instr.(*ssa.SSAReturn) if !ok { continue } for i, rv := range ret.Results { if rv == nil { continue } if uop, ok2 := rv.(*ssa.SSAUnOp); ok2 && uop.Op == ssa.OpMul && uop.X != nil && uop.X.SSAName() == allocName { sig := e.curFunc.Signature if sig != nil && sig.Results() != nil && i < sig.Results().Len() { rt := e.llvmType(sig.Results().At(i).Type()) if rt != "void" && rt != "ptr" && rt != "" { return rt } } return "" } } } } return "" } func (e *irEmitter) inferAllocTypeFromCallArgs(a *ssa.SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*ssa.SSAUnOp); ok && uop.Op == ssa.OpMul && uop.X != nil && uop.X.SSAName() == allocName { loadNames[uop.SSAName()] = true } } } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { call, ok := instr.(*ssa.SSACall) if !ok { continue } callee := call.Call.Value if callee == nil { continue } var sig *types.Signature if cfn, ok2 := callee.(*ssa.SSAFunction); ok2 && cfn.Signature != nil { sig = cfn.Signature } else { if okv, okok := types.SafeUnderlying(callee.SSAType()).(*types.Signature); okok { sig = okv } } if sig == nil || sig.Params() == nil { continue } recvOff := 0 if sig.Recv() != nil { recvOff = 1 } for i, arg := range call.Call.Args { if arg == nil { continue } if !loadNames[arg.SSAName()] { continue } sigIdx := i - recvOff if sigIdx < 0 || sigIdx >= sig.Params().Len() { continue } pt := e.llvmType(sig.Params().At(sigIdx).Type()) if pt != "void" && pt != "ptr" && pt != "" && len(pt) > 0 && pt[0] == '{' { return pt } } } } return "" } func (e *irEmitter) inferAllocTypeFromAppendUsage(a *ssa.SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*ssa.SSAUnOp); ok && uop.Op == ssa.OpMul && uop.X != nil && uop.X.SSAName() == allocName { loadNames[uop.SSAName()] = true } } } if len(loadNames) == 0 { return "" } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { call, ok := instr.(*ssa.SSACall) if !ok { continue } bi, ok2 := call.Call.Value.(*ssa.SSABuiltin) if !ok2 || bi.SSAName() != "append" { continue } if len(call.Call.Args) < 2 { continue } for j := 1; j < len(call.Call.Args); j++ { arg := call.Call.Args[j] if arg == nil { continue } if !loadNames[arg.SSAName()] { continue } sliceArg := call.Call.Args[0] if sl, ok3 := types.SafeUnderlying(sliceArg.SSAType()).(*types.Slice); ok3 { et := e.llvmType(sl.Elem()) if et != "" && et != "void" && et != "ptr" && len(et) > 0 && et[0] == '{' { return et } } if sl, ok3 := sliceArg.SSAType().(*types.Slice); ok3 { et := e.llvmType(sl.Elem()) if et != "" && et != "void" && et != "ptr" && len(et) > 0 && et[0] == '{' { return et } } } } } return "" } func (e *irEmitter) inferAllocTypeFromFieldAddrs(a *ssa.SSAAlloc, baseType string) string { allocName := a.SSAName() names := map[string]bool{allocName: true} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { if uop, ok := instr.(*ssa.SSAUnOp); ok && uop.Op == ssa.OpMul && uop.X != nil && uop.X.SSAName() == allocName { names[uop.SSAName()] = true } } } maxField := -1 fieldTypes := map[int32]string{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { fa, ok := instr.(*ssa.SSAFieldAddr) if !ok || fa.X == nil || !names[fa.X.SSAName()] { continue } if fa.Field > maxField { maxField = fa.Field } faName := fa.SSAName() for _, b2 := range e.curFunc.Blocks { for _, i2 := range b2.Instrs { if s, ok2 := i2.(*ssa.SSAStore); ok2 && s.Addr != nil && s.Val != nil && s.Addr.SSAName() == faName { ft := e.llvmType(s.Val.SSAType()) if ft != "void" && ft != "" { fieldTypes[fa.Field] = ft } } if ld, ok2 := i2.(*ssa.SSAUnOp); ok2 && ld.Op == ssa.OpMul && ld.X != nil && ld.X.SSAName() == faName { ft := e.llvmType(ld.SSAType()) if ft != "void" && ft != "" && ft != "ptr" { if _, exists := fieldTypes[fa.Field]; !exists { fieldTypes[fa.Field] = ft } } } } } } } if maxField < 0 { return "" } baseFields := parseStructFields(baseType) top := maxField if len(baseFields)-1 > top { top = len(baseFields) - 1 } s := "{" for i := 0; i <= top; i++ { if i > 0 { s = s | ", " } ft, ok := fieldTypes[i] if !ok { if i < len(baseFields) && baseFields[i] != "" { ft = baseFields[i] } else { ft = "ptr" } } else if i < len(baseFields) && baseFields[i] != "" { bw := irParseIntWidth(baseFields[i]) fw := irParseIntWidth(ft) if bw > 0 && fw > 0 && bw > fw { ft = baseFields[i] } } s = s | ft } return s | "}" } func parseStructFields(s string) []string { if len(s) < 2 || s[0] != '{' || s[len(s)-1] != '}' { return nil } inner := s[1 : len(s)-1] var fields []string depth := 0 start := int32(0) for i := int32(0); i < int32(len(inner)); i++ { switch inner[i] { case '{': depth++ case '}': depth-- case ',': if depth == 0 { f := llvmTrimSpace(string(inner[start:i])) fields = append(fields, f) start = i + 1 } } } f := llvmTrimSpace(string(inner[start:])) if f != "" { fields = append(fields, f) } return fields } func llvmTrimSpace(s string) string { i := int32(0) for i < int32(len(s)) && s[i] == ' ' { i++ } j := int32(len(s)) for j > i && s[j-1] == ' ' { j-- } return string(s[i:j]) } func (e *irEmitter) inferAllocTypeFromUsage(a *ssa.SSAAlloc) string { allocName := a.SSAName() loadNames := map[string]bool{} for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { load, ok := instr.(*ssa.SSAUnOp) if !ok || load.Op != ssa.OpMul { continue } if load.X != nil && load.X.SSAName() == allocName { loadNames[load.SSAName()] = true } } } if len(loadNames) == 0 { return "ptr" } for _, b := range e.curFunc.Blocks { for _, instr := range b.Instrs { switch u := instr.(type) { case *ssa.SSASlice: if u.X != nil && loadNames[u.X.SSAName()] { return e.sliceType() } case *ssa.SSAIndexAddr: if u.X != nil && loadNames[u.X.SSAName()] { return e.sliceType() } case *ssa.SSACall: for _, arg := range u.Call.Args { if arg != nil && loadNames[arg.SSAName()] { if bi, ok2 := u.Call.Value.(*ssa.SSABuiltin); ok2 { nm := bi.SSAName() if nm == "append" || nm == "copy" || nm == "len" || nm == "cap" { return e.sliceType() } } } } } } } return "ptr" } func (e *irEmitter) emitStore(s *ssa.SSAStore) { if s.Val == nil || s.Addr == nil { e.w(" ; store with nil val/addr\n") return } valType := e.llvmType(s.Val.SSAType()) val := e.operand(s.Val) if load, ok := s.Val.(*ssa.SSAUnOp); ok && load.Op == ssa.OpMul { if g, ok2 := load.X.(*ssa.SSAGlobal); ok2 { valType = e.resolveGlobalDeclType(g) } } if _, isAlloc := s.Val.(*ssa.SSAAlloc); !isAlloc { _, isIndexAddr := s.Val.(*ssa.SSAIndexAddr) _, isExtract := s.Val.(*ssa.SSAExtract) if at, ok := e.allocTypes[s.Val]; ok && at != valType && !isIndexAddr { bothScalar := len(valType) > 0 && valType[0] == 'i' && len(at) > 0 && at[0] == 'i' if !bothScalar || isExtract { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } else if irParseIntWidth(at) > irParseIntWidth(valType) { valType = at } } } if len(valType) > 0 && (valType[0] == '[' || valType[0] == '{') { if addrAt, ok := e.allocTypes[s.Addr]; ok && addrAt != valType { if len(valType) >= len(addrAt) || (valType[0] == '[' && addrAt[0] == '{') { e.allocTypes[s.Addr] = valType } } } if valType == "void" { if at, ok := e.allocTypes[s.Addr]; ok && at != "ptr" && at != "void" { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } else if valType == "ptr" { if uop, ok := s.Val.(*ssa.SSAUnOp); ok && uop.Op == ssa.OpMul { if at, ok2 := e.allocTypes[s.Addr]; ok2 && at != "ptr" && at != "void" { valType = at if val == "null" && valType != "ptr" { val = "zeroinitializer" } } } } if valType == "void" { if _, isFV := s.Addr.(*ssa.SSAFreeVar); isFV { valType = e.llvmType(s.Addr.SSAType()) } else if p, ok := types.SafeUnderlying(s.Addr.SSAType()).(*types.Pointer); ok { valType = e.llvmType(p.Elem()) } if valType == "void" { valType = "ptr" } if val == "null" && valType != "ptr" { val = "zeroinitializer" } } addr := e.operand(s.Addr) if at, ok := e.allocTypes[s.Addr]; ok && (at == "double" || at == "float") && len(valType) > 0 && valType[0] == 'i' { if isConstOperand(val) { val = ensureTokenFloatLit(val) } else { e.nextReg++ conv := "%si2f" | irItoa(e.nextReg) e.w(" ") ; e.w(conv) ; e.w(" = sitofp ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(at) ; e.w("\n") val = conv } valType = at } if at, ok2 := e.allocTypes[s.Addr]; ok2 && len(at) > 0 && at[0] == '{' && len(valType) > 0 && valType[0] == 'i' { if val == "0" || val == "zeroinitializer" { val = "zeroinitializer" valType = at } } if at, ok2 := e.allocTypes[s.Addr]; ok2 && len(at) > 0 && at[0] == 'i' && len(valType) > 0 && valType[0] == '{' { valType = at val = "zeroinitializer" } addrElemT := "" if p, ok := types.SafeUnderlying(s.Addr.SSAType()).(*types.Pointer); ok { addrElemT = e.llvmType(p.Elem()) } if addrElemT == "" || addrElemT == "void" { if at, ok2 := e.allocTypes[s.Addr]; ok2 && at != "ptr" && at != "void" { addrElemT = at } } if len(addrElemT) > 1 && addrElemT[0] == 'i' && len(valType) > 1 && valType[0] == 'i' && addrElemT != valType { elemT := addrElemT vw := irParseIntWidth(valType) ew := irParseIntWidth(elemT) if ew > 0 && vw > ew { e.nextReg++ trunc := "%tr" | irItoa(e.nextReg) e.w(" ") ; e.w(trunc) ; e.w(" = trunc ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(elemT) ; e.w("\n") val = trunc valType = elemT } else if ew > 0 && vw > 0 && vw < ew { if c, ok2 := s.Val.(*ssa.SSAConst); ok2 { if ci, ok3 := c.Value().(*types.ConstInt); ok3 { val = irItoa64(ci.V) valType = elemT } else { valType = elemT } } else { e.nextReg++ ext := "%se" | irItoa(e.nextReg) extOp := "sext" if p2, ok3 := types.SafeUnderlying(s.Addr.SSAType()).(*types.Pointer); ok3 { if b, ok4 := types.SafeUnderlying(p2.Elem()).(*types.Basic); ok4 && b.Info()&types.IsUnsigned != 0 { extOp = "zext" } } e.w(" ") ; e.w(ext) ; e.w(" = ") ; e.w(extOp) ; e.w(" ") ; e.w(valType) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(elemT) ; e.w("\n") val = ext valType = elemT } } } if len(val) > 0 && val[0] == '%' && len(valType) > 1 && valType[0] == 'i' { if rt, ok := e.regTypes[val]; ok && len(rt) > 1 && rt[0] == 'i' && rt != valType { rw := irParseIntWidth(rt) tw := irParseIntWidth(valType) if rw > 0 && tw > 0 && rw > tw { e.nextReg++ trunc := "%stf" | irItoa(e.nextReg) e.w(" ") ; e.w(trunc) ; e.w(" = trunc ") ; e.w(rt) ; e.w(" ") ; e.w(val) ; e.w(" to ") ; e.w(valType) ; e.w("\n") val = trunc } } } e.emitScopeRelocateOnStore(s, val, valType) if val == "zeroinitializer" && len(valType) > 0 && valType[0] == '[' && llvmArrayByteSize(valType) >= 1024 { e.emitZeroInit(valType, addr) } else { 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 syntax.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 rt == "float" { e.w(" ") ; e.w(reg) ; e.w(" = fadd float 0.0, 0.0\n") } else if rt == "double" { e.w(" ") ; e.w(reg) ; e.w(" = fadd double 0.0, 0.0\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") } }