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) emitPhi(p *ssa.SSAPhi) { reg := e.regName(p) typ := e.llvmType(p.SSAType()) e.w(" ") e.w(reg) e.w(" = phi ") e.w(typ) e.w(" ") blk := p.InstrBlock() if blk == nil { return } for i, edge := range p.Edges { if i > 0 { e.w(", ") } e.w("[") e.w(e.operand(edge)) e.w(", ") if blk != nil && i < len(blk.Preds) { pred := blk.Preds[i] if pred != nil { if exitLbl, ok := e.blockExitLabel[pred.Index]; ok { e.w(exitLbl) } else { e.w(e.blockLabel(pred)) } } else { e.w("%unknown") } } else { e.w("%unknown") } e.w("]") } e.w("\n") } func isNumericLiteral(s string) bool { if len(s) == 0 { return false } c := s[0] if c == '-' && len(s) > 1 { c = s[1] } return c >= '0' && c <= '9' } func (e *irEmitter) coerceInt(valReg string, fromType string, toType string) string { if fromType == toType { return valReg } fromBits := e.intBits(fromType) toBits := e.intBits(toType) if fromBits == 0 || toBits == 0 { return valReg } if isNumericLiteral(valReg) { return valReg } e.nextReg++ r := "%rc" | irItoa(e.nextReg) if fromBits > toBits { e.w(" ") ; e.w(r) ; e.w(" = trunc ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") } else { e.w(" ") ; e.w(r) ; e.w(" = sext ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") } return r } func (e *irEmitter) intBits(ty string) int32 { switch ty { case "i1": return 1 case "i8": return 8 case "i16": return 16 case "i32": return 32 case "i64": return 64 } return 0 } func (e *irEmitter) intToFloat(valReg string, fromType string, toType string) string { if rt, ok := e.regTypes[valReg]; ok && (rt == "double" || rt == "float") { return valReg } e.nextReg++ r := "%itf" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = sitofp ") ; e.w(fromType) ; e.w(" ") ; e.w(valReg) ; e.w(" to ") ; e.w(toType) ; e.w("\n") return r } func (e *irEmitter) floatBinOp(op ssa.SSAOp) string { switch op { case ssa.OpAdd: return "fadd" case ssa.OpSub: return "fsub" case ssa.OpMul: return "fmul" case ssa.OpQuo: return "fdiv" case ssa.OpEql: return "fcmp oeq" case ssa.OpNeq: return "fcmp une" case ssa.OpLss: return "fcmp olt" case ssa.OpGtr: return "fcmp ogt" case ssa.OpLeq: return "fcmp ole" case ssa.OpGeq: return "fcmp oge" } return "fadd" } func (e *irEmitter) arrayElemType(arrType string) string { // "[6 x double]" -> "double" xPos := -1 for i := 0; i < len(arrType); i++ { if arrType[i] == 'x' && i > 0 && arrType[i-1] == ' ' { xPos = i break } } if xPos < 0 || xPos+2 >= len(arrType) { return arrType } end := len(arrType) if arrType[end-1] == ']' { end = end - 1 } return arrType[xPos+2 : end] } func (e *irEmitter) namedResultType(nr *ssa.SSAAlloc) string { if p, ok := types.SafeUnderlying(nr.SSAType()).(*types.Pointer); ok && p.Elem() != nil { return e.llvmType(p.Elem()) } return e.llvmType(nr.SSAType()) } func (e *irEmitter) emitReturn(r *ssa.SSAReturn) { if len(e.deferList) > 0 { e.emitRunDefers() } e.deallocBeforeReturn(r) frt := e.funcRetType(e.curFunc) if len(r.Results) == 0 { rt := e.funcRetType(e.curFunc) if rt == "void" { e.w(" ret void\n") } else if len(e.curFunc.NamedResults) > 0 { if len(e.curFunc.NamedResults) == 1 { nr := e.curFunc.NamedResults[0] nrt := e.namedResultType(nr) e.nextReg++ tmp := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n") e.w(" ret ") ; e.w(nrt) ; e.w(" ") ; e.w(tmp) ; e.w("\n") } else { retType := rt e.nextReg++ agg := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(agg) ; e.w(" = alloca ") ; e.w(retType) ; e.w("\n") e.w(" store ") ; e.w(retType) ; e.w(" zeroinitializer, ptr ") ; e.w(agg) ; e.w("\n") for i, nr := range e.curFunc.NamedResults { nrt := e.namedResultType(nr) e.nextReg++ tmp := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(tmp) ; e.w(" = load ") ; e.w(nrt) ; e.w(", ptr ") ; e.w(e.regName(nr)) ; e.w("\n") e.nextReg++ gep := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(gep) ; e.w(" = getelementptr ") ; e.w(retType) ; e.w(", ptr ") ; e.w(agg) ; e.w(", i32 0, i32 ") ; e.w(irItoa(i)) ; e.w("\n") e.w(" store ") ; e.w(nrt) ; e.w(" ") ; e.w(tmp) ; e.w(", ptr ") ; e.w(gep) ; e.w("\n") } e.nextReg++ rv := "%nr" | irItoa(e.nextReg) e.w(" ") ; e.w(rv) ; e.w(" = load ") ; e.w(retType) ; e.w(", ptr ") ; e.w(agg) ; e.w("\n") e.w(" ret ") ; e.w(retType) ; e.w(" ") ; e.w(rv) ; e.w("\n") } } else { e.w(" ret ") ; e.w(rt) ; e.w(" zeroinitializer\n") } return } sig := e.curFunc.Signature if len(r.Results) == 1 { typ := e.llvmType(r.Results[0].SSAType()) rtyp := e.resolvedType(r.Results[0], typ) if rtyp != typ { typ = rtyp } val := e.operand(r.Results[0]) expectType := typ if sig != nil && sig.Results() != nil && sig.Results().Len() == 1 { expectType = e.llvmType(sig.Results().At(0).Type()) } if typ == "void" { typ = frt } if expectType == "void" { expectType = frt } if val == "null" && expectType != "ptr" { val = "zeroinitializer" } else { val = e.coerceInt(val, typ, expectType) } if typ != expectType && val != "zeroinitializer" { if expectType == "ptr" && e.intBits(typ) > 0 { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = inttoptr ") ; e.w(typ) ; e.w(" ") ; e.w(val) ; e.w(" to ptr\n") val = rc typ = "ptr" } else if typ == "ptr" && e.intBits(expectType) > 0 { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = ptrtoint ptr ") ; e.w(val) ; e.w(" to ") ; e.w(expectType) ; e.w("\n") val = rc typ = expectType } else if typ == "double" && expectType == "float" { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = fptrunc double ") ; e.w(val) ; e.w(" to float\n") val = rc typ = "float" } else if typ == "float" && expectType == "double" { e.nextReg++ rc := "%rc" | irItoa(e.nextReg) e.w(" ") ; e.w(rc) ; e.w(" = fpext float ") ; e.w(val) ; e.w(" to double\n") val = rc typ = "double" } if typ != expectType { val = "zeroinitializer" } } e.w(" ret ") e.w(expectType) e.w(" ") e.w(val) e.w("\n") return } var expectTypes []string if sig != nil && sig.Results() != nil { for i := 0; i < sig.Results().Len(); i++ { expectTypes = append(expectTypes, e.resolveResultType(sig.Results().At(i).Type())) } } retType := "{" for i := 0; i < len(r.Results); i++ { res := r.Results[i] if i > 0 { retType = retType | ", " } if i < len(expectTypes) { retType = retType | expectTypes[i] } else { retType = retType | e.llvmType(res.SSAType()) } } retType = retType | "}" prev := "undef" for i := 0; i < len(r.Results); i++ { res := r.Results[i] valType := e.llvmType(res.SSAType()) valOp := e.operand(res) if _, ok := res.(*ssa.SSACall); ok && i < len(expectTypes) { if valType != expectTypes[i] && len(valType) > 0 && valType[0] == '{' { field := extractTupleField(valType, i) if field == expectTypes[i] { e.nextReg++ exReg := "%rex" | irItoa(e.nextReg) e.w(" ") ; e.w(exReg) ; e.w(" = extractvalue ") ; e.w(valType) ; e.w(" ") ; e.w(valOp) ; e.w(", ") ; e.w(irItoa(i)) ; e.w("\n") valOp = exReg valType = expectTypes[i] } } } elemType := valType if i < len(expectTypes) { elemType = expectTypes[i] if valOp == "null" && elemType != "ptr" { valOp = "zeroinitializer" } else if (elemType == "double" || elemType == "float") && isConstOperand(valOp) { valOp = ensureTokenFloatLit(valOp) } else if (elemType == "double" || elemType == "float") && e.intBits(valType) > 0 { valOp = e.intToFloat(valOp, valType, elemType) } else if valType == "double" && elemType == "float" { e.nextReg++ fc := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(fc) ; e.w(" = fptrunc double ") ; e.w(valOp) ; e.w(" to float\n") valOp = fc } else if valType == "float" && elemType == "double" { e.nextReg++ fc := "%fc" | irItoa(e.nextReg) e.w(" ") ; e.w(fc) ; e.w(" = fpext float ") ; e.w(valOp) ; e.w(" to double\n") valOp = fc } 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 blockHasReturn(b *ssa.SSABasicBlock) bool { for _, instr := range b.Instrs { if _, ok := instr.(*ssa.SSAReturn); ok { return true } } return false } func (e *irEmitter) emitJump(j *ssa.SSAJump) { blk := j.InstrBlock() if blk == nil { return } if len(blk.Succs) > 0 { target := blk.Succs[0] if blk.ScopeID > 0 && target.ScopeID != blk.ScopeID && !blockHasReturn(target) { e.emitScopeFreesTo(blk.ScopeID) } e.w(" br label ") e.w(e.blockLabel(target)) e.w("\n") } } func isComparisonOp(op ssa.SSAOp) bool { return op == ssa.OpEql || op == ssa.OpNeq || op == ssa.OpLss || op == ssa.OpLeq || op == ssa.OpGtr || op == ssa.OpGeq } func (e *irEmitter) emitIf(i *ssa.SSAIf) { blk := i.InstrBlock() if i.Cond == nil { if len(blk.Succs) >= 2 { e.w(" br label ") e.w(e.blockLabel(blk.Succs[1])) e.w("\n") } else { e.w(" unreachable\n") } return } cond := e.operand(i.Cond) condType := e.llvmType(i.Cond.SSAType()) if at, ok := e.allocTypes[i.Cond]; ok { condType = at } if bop, ok := i.Cond.(*ssa.SSABinOp); ok && isComparisonOp(bop.Op) { condType = "i1" } if cond == "true" || cond == "false" { condType = "i1" } if _, isNot := i.Cond.(*ssa.SSAUnOp); isNot { if rt, ok2 := e.regTypes[e.regName(i.Cond)]; ok2 && rt == "i1" { condType = "i1" } } if condType != "i1" && condType != "" && condType != "void" { e.nextReg++ truncReg := "%ift" | irItoa(e.nextReg) if condType == "ptr" { e.w(" ") ; e.w(truncReg) ; e.w(" = icmp ne ptr ") ; e.w(cond) ; e.w(", null\n") } else if len(condType) > 0 && condType[0] == '{' { e.nextReg++ extReg := "%ife" | irItoa(e.nextReg) e.w(" ") ; e.w(extReg) ; e.w(" = extractvalue ") ; e.w(condType) ; e.w(" ") ; e.w(cond) ; e.w(", 0\n") e.w(" ") ; e.w(truncReg) ; e.w(" = icmp ne ptr ") ; e.w(extReg) ; e.w(", null\n") } else { e.w(" ") ; e.w(truncReg) ; e.w(" = trunc ") ; e.w(condType) ; e.w(" ") ; e.w(cond) ; e.w(" to i1\n") } cond = truncReg } if len(blk.Succs) >= 2 { e.w(" br i1 ") e.w(cond) e.w(", label ") e.w(e.blockLabel(blk.Succs[0])) e.w(", label ") e.w(e.blockLabel(blk.Succs[1])) e.w("\n") } } func (e *irEmitter) emitConvert(c *ssa.SSAConvert) { reg := e.regName(c) srcType := e.llvmType(c.X.SSAType()) dstType := e.llvmType(c.SSAType()) val := e.operand(c.X) if srcType != "ptr" { resolved := e.resolvedType(c.X, srcType) if resolved != srcType { srcType = resolved } } if srcType == "void" || c.X.SSAType() == nil { if dstType == "ptr" { e.valName[c] = "null" } else { e.valName[c] = "zeroinitializer" } return } if srcType == dstType { e.valName[c] = val e.allocTypes[c] = srcType return } if srcType == e.sliceType() && len(dstType) > 0 && dstType[0] == '[' { dp := e.nextReg2("cv") e.w(" ") ; e.w(dp) ; e.w(" = extractvalue ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = load ") ; e.w(dstType) ; e.w(", ptr ") ; e.w(dp) ; e.w("\n") return } srcIsInt := false if b, ok := types.SafeUnderlying(c.X.SSAType()).(*types.Basic); ok { srcIsInt = b.Info()&types.IsInteger != 0 } if !srcIsInt && len(srcType) > 0 && srcType[0] == 'i' { srcIsInt = true } if (e.isStringLike(c.SSAType()) || dstType == e.sliceType()) && srcIsInt { if k, ok := c.X.(*ssa.SSAConst); ok { rv := int64(0) if ci, ok2 := k.Value().(*types.ConstInt); ok2 { rv = ci.V } s := runeToUTF8(rune(rv)) idx := e.addStringConst(s) ipt := e.intptrType() slen := irItoa64(int64(len(s))) e.valName[c] = "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }" return } e.declareRuntime("runtime.stringFromUnicode", e.sliceType(), "i32, ptr") srcVal := val if srcType != "i32" { e.nextReg++ srcVal = "%cv" | irItoa(e.nextReg) if e.typeBits(c.X.SSAType()) < 32 { e.w(" ") ; e.w(srcVal) ; e.w(" = sext ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(" to i32\n") } else if e.typeBits(c.X.SSAType()) > 32 { e.w(" ") ; e.w(srcVal) ; e.w(" = trunc ") ; e.w(srcType) ; e.w(" ") ; e.w(val) ; e.w(" to i32\n") } } e.w(" ") ; e.w(reg) ; e.w(" = call ") ; e.w(e.sliceType()) ; e.w(" @runtime.stringFromUnicode(i32 ") ; e.w(srcVal) ; e.w(", ptr null)\n") return } op := e.conversionOp(c.X.SSAType(), c.SSAType()) srcBitsLLVM := e.intBits(srcType) dstBitsLLVM := e.intBits(dstType) if (op == "sext" || op == "zext") && srcBitsLLVM > 0 && dstBitsLLVM > 0 && srcBitsLLVM > dstBitsLLVM { op = "trunc" } else if op == "trunc" && srcBitsLLVM > 0 && dstBitsLLVM > 0 && srcBitsLLVM < dstBitsLLVM { op = "sext" } srcIsFloat := srcType == "double" || srcType == "float" dstIsFloat := dstType == "double" || dstType == "float" if op == "trunc" && srcIsFloat && !dstIsFloat { op = "fptosi" } else if op == "trunc" && !srcIsFloat && dstIsFloat { op = "sitofp" } else if (op == "sext" || op == "zext") && !srcIsFloat && dstIsFloat { op = "sitofp" } else if (op == "sext" || op == "zext") && srcIsFloat && !dstIsFloat { op = "fptosi" } else if op == "bitcast" && srcIsFloat != dstIsFloat { if srcIsFloat { op = "fptosi" } else { op = "sitofp" } } else if (op == "sext" || op == "zext" || op == "trunc") && srcIsFloat && dstIsFloat { if e.intBits(srcType) < e.intBits(dstType) { op = "fpext" } else { op = "fptrunc" } } if op == "ptrtoint" && e.intBits(dstType) == 0 { if dstType == e.ifaceType() { typeid := e.typeIDGlobal(c.X.SSAType()) t1 := e.nextReg2("cv") e.w(" ") ; e.w(t1) ; e.w(" = insertvalue {ptr, ptr} undef, ptr ") ; e.w(typeid) ; e.w(", 0\n") e.w(" ") ; e.w(reg) ; e.w(" = insertvalue {ptr, ptr} ") ; e.w(t1) ; e.w(", ptr ") ; e.w(val) ; e.w(", 1\n") } else { e.valName[c] = "zeroinitializer" } return } if op == "inttoptr" && e.intBits(srcType) == 0 { if srcType == e.ifaceType() { e.nextReg++ r := "%cv" | irItoa(e.nextReg) e.w(" ") ; e.w(r) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(val) ; e.w(", 1\n") e.valName[c] = r } else { e.valName[c] = "null" } return } e.w(" ") e.w(reg) e.w(" = ") e.w(op) e.w(" ") e.w(srcType) e.w(" ") e.w(val) e.w(" to ") e.w(dstType) e.w("\n") if e.intBits(dstType) > 0 || dstType == "ptr" { e.setRegType(c, reg, dstType) } } func (e *irEmitter) conversionOp(from, to syntax.Type) string { fromBits := e.typeBits(from) toBits := e.typeBits(to) fromFloat := false toFloat := false fromSigned := true if b, ok := types.SafeUnderlying(from).(*types.Basic); ok { fromFloat = b.Info()&types.IsFloat != 0 if b.Info()&types.IsUnsigned != 0 { fromSigned = false } } if b, ok := types.SafeUnderlying(to).(*types.Basic); ok { toFloat = b.Info()&types.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 := types.SafeUnderlying(from).(*types.Pointer) _, toPtr := types.SafeUnderlying(to).(*types.Pointer) if !fromPtr && e.llvmType(from) == "ptr" { fromPtr = true } if !toPtr && e.llvmType(to) == "ptr" { toPtr = true } if fromPtr && !toPtr { return "ptrtoint" } if !fromPtr && toPtr { return "inttoptr" } if fromBits < toBits { toUnsigned := false if b, ok := types.SafeUnderlying(to).(*types.Basic); ok && b.Info()&types.IsUnsigned != 0 { toUnsigned = true } if fromSigned && !toUnsigned { return "sext" } return "zext" } if fromBits > toBits { return "trunc" } return "bitcast" } func (e *irEmitter) typeBits(t syntax.Type) int32 { if t == nil { return 0 } switch t := types.SafeUnderlying(t).(type) { case *types.Basic: switch t.Kind() { case types.Bool: return 1 case types.Int8, types.Uint8: return 8 case types.Int16, types.Uint16: return 16 case types.Int32, types.Uint32: return 32 case types.Int64, types.Uint64: return 64 case types.Float32: return 32 case types.Float64: return 64 case types.UntypedInt, types.UntypedRune: return 32 case types.UntypedFloat: return 64 case types.UnsafePointer: return e.ptrBits } case *types.Pointer: return e.ptrBits } return 0 } func (e *irEmitter) emitChangeType(c *ssa.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") }