func checkNoSideEffects(x *cc.Expr, mode int) { var before, after []*cc.Stmt old := x.String() doSideEffects(x, &before, &after, mode) if len(before)+len(after) > 0 { fprintf(x.Span, "cannot handle side effects in %s", old) } }
func fixMerge(dst, src *cc.Expr) { syn := dst.SyntaxInfo syn.Comments.Before = append(syn.Comments.Before, src.Comments.Before...) syn.Comments.After = append(syn.Comments.After, src.Comments.After...) syn.Comments.Suffix = append(syn.Comments.Suffix, src.Comments.Suffix...) *dst = *src dst.SyntaxInfo = syn }
func matchSize(fn *cc.Decl, obj *cc.Expr, objType *cc.Type, siz *cc.Expr) bool { switch siz.Op { default: return false case cc.SizeofType: // ok if sizeof type of first arg return sameType(siz.Type, objType) case cc.SizeofExpr: // ok if sizeof *firstarg y := siz.Left if y.Op == cc.Paren { y = y.Left } return obj.String() == y.String() } }
func tryPrintf(curfn *cc.Decl, x *cc.Expr, name string, fmtpos int, newName string) bool { if (x.Left.Text == name || (strings.Contains(name, ".") || strings.Contains(name, "->")) && x.Left.String() == name) && len(x.List) >= fmtpos+1 && x.List[fmtpos].Op == cc.String { x.List = append(x.List[:fmtpos+1], fixPrintFormat(curfn, x.List[fmtpos], x.List[fmtpos+1:])...) if newName != "" { x.Left.Text = newName x.Left.XDecl = nil } return true } return false }
func fixSpecialCall(fn *cc.Decl, x *cc.Expr, targ *cc.Type) bool { if x.Left.Op != cc.Name { return false } switch x.Left.Text { case "memmove": if len(x.List) != 3 { // fprintf(x.Span, "unsupported %v", x) return false } siz := x.List[2] if siz.Op == cc.Number && siz.Text == "4" { obj1, obj1Type := objIndir(fn, x.List[0]) obj2, obj2Type := objIndir(fn, x.List[1]) if obj1Type == nil || obj2Type == nil { // fprintf(x.Span, "unsupported %v - missing types", x) return true } if (obj1Type.Kind == Uint32 || obj1Type.Kind == Int32) && obj2Type.Kind == Float32 { x.Op = cc.Eq x.Left = obj1 x.Right = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "math.Float32bits", }, List: []*cc.Expr{obj2}, } x.XType = uint32Type return true } // fprintf(x.Span, "unsupported %v - size 4 type %v %v", x, GoString(obj1Type), GoString(obj2Type)) } if siz.Op == cc.Number && siz.Text == "8" { obj1, obj1Type := objIndir(fn, x.List[0]) obj2, obj2Type := objIndir(fn, x.List[1]) if obj1Type == nil || obj2Type == nil { // fprintf(x.Span, "unsupported %v - missing types", x) return true } if (obj1Type.Kind == Uint64 || obj1Type.Kind == Int64) && obj2Type.Kind == Float64 { x.Op = cc.Eq x.Left = obj1 x.Right = &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "math.Float64bits", }, List: []*cc.Expr{obj2}, } x.XType = uint64Type return true } // fprintf(x.Span, "unsupported %v - size 8 type %v %v", x, GoString(obj1Type), GoString(obj2Type)) } if siz.Op == cc.SizeofExpr { obj1Type := fixGoTypesExpr(fn, x.List[0], nil) obj2Type := fixGoTypesExpr(fn, x.List[1], nil) sizeType := fixGoTypesExpr(fn, siz.Left, nil) if obj1Type == nil || obj2Type == nil { // fprintf(x.Span, "unsupported %v - bad types", x) return true } if obj2Type.Kind == cc.Array && sameType(obj2Type, sizeType) || obj2Type.Kind == Slice && GoString(x.List[1]) == GoString(siz.Left) { x.Left.Text = "copy" x.Left.XDecl = nil x.List = x.List[:2] return true } // fprintf(x.Span, "unsupported %v - not array %v %v", x, GoString(obj2Type), GoString(sizeType)) return true } left := fixGoTypesExpr(fn, x.List[0], nil) right := fixGoTypesExpr(fn, x.List[1], nil) fixGoTypesExpr(fn, siz, nil) if isSliceOrArray(left) && isSliceOrArray(right) && left.Base.Is(Uint8) && right.Base.Is(Uint8) { x.Left.Text = "copy" x.Left.XDecl = nil if x.List[1].Op == ExprSlice && x.List[1].List[1] == nil { x.List[1].List[2] = siz } else { x.List[1] = &cc.Expr{Op: ExprSlice, List: []*cc.Expr{x.List[1], nil, siz}} } x.List = x.List[:2] return true } // fprintf(x.Span, "unsupported %v (%v %v)", x, GoString(left), GoString(right)) return true case "mal", "malloc", "emallocz", "xmalloc": if len(x.List) != 1 { fprintf(x.Span, "unsupported %v - too many args", x) return false } siz := x.List[0] var count *cc.Expr if siz.Op == cc.Mul { count = siz.Left siz = siz.Right if count.Op == cc.SizeofType || count.Op == cc.SizeofExpr { count, siz = siz, count } } var typ *cc.Type switch siz.Op { default: typ = byteType count = siz case cc.SizeofExpr: typ = fixGoTypesExpr(fn, siz.Left, nil) if typ == nil { fprintf(siz.Span, "failed to type check %v", siz.Left) } case cc.SizeofType: typ = siz.Type if typ == nil { fprintf(siz.Span, "sizeoftype missing type") } } if typ == nil { fprintf(x.Span, "unsupported %v - cannot understand type", x) return true } if count == nil { x.Left.Text = "new" x.Left.XDecl = nil x.List = []*cc.Expr{&cc.Expr{Op: ExprType, Type: typ}} x.XType = &cc.Type{Kind: cc.Ptr, Base: typ} if typ.String() == "Prog" { isGC := strings.Contains(x.Span.Start.File, "cmd/gc") isCompiler := isGC || strings.Contains(x.Span.Start.File, "cmd/6g") || strings.Contains(x.Span.Start.File, "cmd/8g") || strings.Contains(x.Span.Start.File, "cmd/5g") || strings.Contains(x.Span.Start.File, "cmd/9g") if isCompiler { x.List = nil x.Left.Text = "Ctxt.NewProg" if !isGC { x.Left.Text = "gc." + x.Left.Text } } } } else { x.Left.Text = "make" x.Left.XDecl = nil x.XType = &cc.Type{Kind: Slice, Base: typ} x.List = []*cc.Expr{ &cc.Expr{Op: ExprType, Type: x.XType}, count, } } return true case "strdup", "estrdup": if len(x.List) != 1 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], stringType) fixMerge(x, x.List[0]) x.XType = stringType return true case "strcpy", "strcat", "fmtstrcpy": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], nil) fixGoTypesExpr(fn, x.List[1], stringType) x.Op = cc.Eq if x.Left.Text == "strcat" || x.Left.Text == "fmtstrcpy" { x.Op = cc.AddEq } x.Left = x.List[0] x.Right = x.List[1] x.XType = stringType return true case "strlen": x.Left.Text = "len" x.Left.XDecl = nil x.XType = intType return true case "strcmp": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } fixGoTypesExpr(fn, x.List[0], stringType) fixGoTypesExpr(fn, x.List[1], stringType) x.Left.Text = "stringsCompare" x.Left.XDecl = nil x.XType = intType return true case "abort": x.Left.Text = "panic" x.Left.XDecl = nil x.List = []*cc.Expr{{Op: cc.Name, Text: `"abort"`}} return true case "TUP", "CASE": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } left := fixGoTypesExpr(fn, x.List[0], targ) right := fixGoTypesExpr(fn, x.List[1], targ) forceConvert(fn, x.List[0], left, uint32Type) forceConvert(fn, x.List[1], right, uint32Type) x.Op = cc.Or x.Left = &cc.Expr{Op: cc.Lsh, Left: x.List[0], Right: &cc.Expr{Op: cc.Number, Text: "16"}, XType: left} x.Right = x.List[1] x.List = nil x.XType = uint32Type return true case "R": if len(x.List) != 2 { fprintf(x.Span, "unsupported %v - too many args", x) return false } left := fixGoTypesExpr(fn, x.List[0], targ) right := fixGoTypesExpr(fn, x.List[1], targ) forceConvert(fn, x.List[0], left, uint32Type) forceConvert(fn, x.List[1], right, uint32Type) x.Op = cc.Or x.Left = x.List[0] x.Right = &cc.Expr{Op: cc.Lsh, Left: x.List[1], Right: &cc.Expr{Op: cc.Number, Text: "24"}, XType: left} x.List = nil x.XType = uint32Type return true case "FCASE": if len(x.List) != 3 { fprintf(x.Span, "unsupported %v - too many args", x) return false } arg0 := fixGoTypesExpr(fn, x.List[0], targ) arg1 := fixGoTypesExpr(fn, x.List[1], targ) arg2 := fixGoTypesExpr(fn, x.List[2], targ) forceConvert(fn, x.List[0], arg0, uint32Type) forceConvert(fn, x.List[1], arg1, uint32Type) forceConvert(fn, x.List[2], arg2, uint32Type) x.Op = cc.Or x.Left = &cc.Expr{Op: cc.Lsh, Left: x.List[0], Right: &cc.Expr{Op: cc.Number, Text: "16"}, XType: uint32Type} x.Right = &cc.Expr{ Op: cc.Or, Left: &cc.Expr{Op: cc.Lsh, Left: x.List[1], Right: &cc.Expr{Op: cc.Number, Text: "8"}, XType: uint32Type}, Right: x.List[2], } x.List = nil x.XType = uint32Type return true } return false }
func forceConvert(fn *cc.Decl, x *cc.Expr, actual, targ *cc.Type) { if isEmptyInterface(targ) { return } if isEmptyInterface(actual) { old := copyExpr(x) x.Op = TypeAssert x.Left = old x.Right = nil x.List = nil x.Type = targ x.XType = targ return } if isNumericConst(x) && targ != nil { switch targ.Kind { case cc.Ptr, Slice: if x.Op == cc.Number && x.Text == "0" { x.Op = cc.Name x.Text = "nil" x.XType = targ } case String: if x.Op == cc.Number && x.Text == "0" { x.Op = cc.Name x.Text = `""` x.XType = targ } } return } if x.Op == cc.Name && x.Text == "nil" && targ != nil { switch targ.Kind { case cc.Func, cc.Ptr, Slice: return case String: x.Text = `""` x.XType = targ x.XDecl = nil return } } if actual == nil || targ == nil { return } if actual.Kind == Ideal && Int8 <= targ.Kind && targ.Kind <= Float64 { return } if x != nil && x.Op == cc.Name && x.Text == "nil" { if targ.Kind == cc.Func || targ.Kind == cc.Ptr || targ.Kind == Slice { return } } // Func conversions are never useful. // If the func types are different, the conversion will fail; // if not, the conversion is unnecessary. // Either way the conversion is an eyesore. if targ.Kind == cc.Func || targ.Kind == cc.Ptr && targ.Base.Kind == cc.Func { return } if actual.Kind == Bool && Int8 <= targ.Kind && targ.Kind <= Float64 { old := copyExpr(x) if targ.Kind == Int { x.Op = cc.Call x.Left = &cc.Expr{Op: cc.Name, Text: "bool2int"} x.List = []*cc.Expr{old} x.Right = nil } else { x.Op = cc.Cast x.Left = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "bool2int"}, List: []*cc.Expr{old}} x.Type = targ } return } if actual.Kind == cc.Array && targ.Kind == Slice && sameType(actual.Base, targ.Base) { old := copyExpr(x) x.Op = ExprSlice x.List = []*cc.Expr{old, nil, nil} x.Left = nil x.Right = nil return } if actual.Kind == Slice && targ.Kind == cc.Ptr && sameType(actual.Base, targ.Base) { if isCall(x, "make") { return } old := copyExpr(x) x.Op = cc.Addr x.Left = &cc.Expr{Op: cc.Index, Left: old, Right: &cc.Expr{Op: cc.Number, Text: "0"}} return } if !sameType(actual, targ) { if x.Op == cc.Twid { forceConvert(fn, x.Left, actual, targ) x.XType = targ return } old := copyExpr(x) // for debugging: // old = &cc.Expr{Op: cc.Cast, Left: old, Type: actual, XType: actual} x.Op = cc.Cast x.Left = old x.Right = nil x.List = nil x.Type = targ x.XType = targ if actual.Kind == cc.Array && targ.Kind == Slice { x.Op = ExprSlice x.List = []*cc.Expr{old, nil, nil} x.Left = nil x.Type = nil } } }
func fixGoTypesExpr(fn *cc.Decl, x *cc.Expr, targ *cc.Type) (ret *cc.Type) { if x == nil { return nil } defer func() { x.XType = ret }() if x.Op == cc.Paren { return fixGoTypesExpr(fn, x.Left, targ) } // Make explicit C's implicit conversions from boolean to non-boolean and vice versa. switch x.Op { case cc.AndAnd, cc.OrOr, cc.Not, cc.EqEq, cc.Lt, cc.LtEq, cc.Gt, cc.GtEq, cc.NotEq: if targ != nil && targ.Kind != Bool { old := copyExpr(x) if targ.Kind == Int { x.Op = cc.Call x.Left = &cc.Expr{Op: cc.Name, Text: "bool2int"} x.List = []*cc.Expr{old} x.Right = nil } else { x.Op = cc.Cast x.Left = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "bool2int"}, List: []*cc.Expr{old}} x.Type = targ } fixGoTypesExpr(fn, old, boolType) return targ } default: if targ != nil && targ.Kind == Bool { old := copyExpr(x) left := fixGoTypesExpr(fn, old, nil) if left != nil && left.Kind == Bool { return targ } if old.Op == cc.Number { switch old.Text { case "1": x.Op = cc.Name x.Text = "true" return targ case "0": x.Op = cc.Name x.Text = "false" return targ } } x.Op = cc.NotEq x.Left = old x.Right = zeroFor(left) fixSpecialCompare(fn, x) return targ } } fixArray(fn, x) switch x.Op { default: panic(fmt.Sprintf("unexpected construct %v in fixGoTypesExpr - %v - %v", GoString(x), x.Op, x.Span)) case ExprType: // inserted by a rewrite return nil case ExprSlice: // inserted by rewriteLen left := fixGoTypesExpr(fn, x.List[0], targ) fixGoTypesExpr(fn, x.List[1], nil) fixGoTypesExpr(fn, x.List[2], nil) return left case cc.Comma: for i, y := range x.List { t := targ if i+1 < len(x.List) { t = nil } fixGoTypesExpr(fn, y, t) } return nil case ExprBlock: for _, stmt := range x.Block { fixGoTypesStmt(nil, fn, stmt) } return nil case cc.Add, cc.And, cc.Div, cc.Mod, cc.Mul, cc.Or, cc.Sub, cc.Xor: if x.Op == cc.Sub && isPtrSliceOrArray(x.Left.XType) && isPtrSliceOrArray(x.Right.XType) { left := fixGoTypesExpr(fn, x.Left, nil) right := fixGoTypesExpr(fn, x.Right, nil) if left != nil && right != nil && left.Kind != right.Kind { if left.Kind == Slice { forceConvert(fn, x.Right, right, left) } else { forceConvert(fn, x.Left, left, right) } } x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}} x.Right = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}} x.Op = cc.Add return intType } left := fixGoTypesExpr(fn, x.Left, targ) if x.Op == cc.And && x.Right.Op == cc.Twid { x.Op = AndNot x.Right = x.Right.Left } if x.Op == cc.Add && isSliceStringOrArray(left) { fixGoTypesExpr(fn, x.Right, nil) x.Op = ExprSlice x.List = []*cc.Expr{x.Left, x.Right, nil} x.Left = nil x.Right = nil if left.Kind == cc.Array { left = &cc.Type{Kind: Slice, Base: left.Base} } return left } right := fixGoTypesExpr(fn, x.Right, targ) return fixBinary(fn, x, left, right, targ) case cc.AddEq, cc.AndEq, cc.DivEq, cc.Eq, cc.ModEq, cc.MulEq, cc.OrEq, cc.SubEq, cc.XorEq: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.AndEq && x.Right.Op == cc.Twid { x.Op = AndNotEq x.Right = x.Right.Left } if x.Op == cc.AddEq && isSliceOrString(left) { fixGoTypesExpr(fn, x.Right, nil) old := copyExpr(x.Left) x.Op = cc.Eq x.Right = &cc.Expr{Op: ExprSlice, List: []*cc.Expr{old, x.Right, nil}} return left } if x.Op == cc.Eq && x.Left.Op == cc.Index && sameType(x.Left.Left.XType, stringType) && GoString(x.Left.Right) == "0" && GoString(x.Right) == "0" { x.Left = x.Left.Left x.Right = &cc.Expr{Op: cc.Name, Text: `""`} return x.Left.XType } forceGoType(fn, x.Right, left) if x.Op == cc.Eq && x.Left != nil && x.Right != nil && x.Right.XType != nil && isCall(x.Right, "make") && x.Left.XDecl != nil && x.Left.XDecl.Type != nil && x.Left.XDecl.Type.Kind == cc.Ptr && sameType(x.Left.XDecl.Type.Base, x.Right.XType.Base) { x.Left.XDecl.Type = x.Right.XType x.Left.XType = x.Right.XType left = x.Right.XType } return left case ColonEq: left := fixGoTypesExpr(fn, x.Right, nil) x.Left.XType = left x.Left.XDecl.Type = left return left case cc.Addr: left := fixGoTypesExpr(fn, x.Left, nil) if left == nil { return nil } if targ != nil && targ.Kind == Slice && sameType(targ.Base, left) { l := x.Left l.Op = ExprSlice l.List = []*cc.Expr{l.Left, l.Right, nil} l.Left = nil l.Right = nil fixMerge(x, l) return targ } return &cc.Type{Kind: cc.Ptr, Base: left} case cc.AndAnd, cc.OrOr, cc.Not: fixGoTypesExpr(fn, x.Left, boolType) if x.Right != nil { fixGoTypesExpr(fn, x.Right, boolType) } return boolType case cc.Arrow, cc.Dot: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.Arrow && isSliceOrString(left) { x.Left = &cc.Expr{Op: cc.Index, Left: x.Left, Right: &cc.Expr{Op: cc.Number, Text: "0"}} } return x.XDecl.Type case cc.Call: if fixPrintf(fn, x) { return x.XType } if fixSpecialCall(fn, x, targ) { return x.XType } left := fixGoTypesExpr(fn, x.Left, nil) if left != nil && left.Kind == cc.Ptr && left.Base.Kind == cc.Func { left = left.Base } for i, y := range x.List { if left != nil && left.Kind == cc.Func && i < len(left.Decls) { forceGoType(fn, y, left.Decls[i].Type) } else { fixGoTypesExpr(fn, y, nil) } } if left != nil && left.Kind == cc.Func { return left.Base } return nil case cc.Cast: fixGoTypesExpr(fn, x.Left, nil) if isEmptyInterface(x.Left.XType) { x.Op = TypeAssert } return x.Type case cc.CastInit: fixGoTypesInit(nil, x.Init) return x.Type case cc.EqEq, cc.Gt, cc.GtEq, cc.Lt, cc.LtEq, cc.NotEq: if fixSpecialCompare(fn, x) { return boolType } left := fixGoTypesExpr(fn, x.Left, nil) if x.Right.Op == cc.Number && x.Right.Text == "0" || x.Right.Op == cc.Name && x.Right.Text == "nil" { if isSliceOrPtr(left) { x.Right.Op = cc.Name x.Right.Text = "nil" return boolType } if left != nil && left.Kind == String { x.Right.Op = cc.String x.Right.Texts = []string{`""`} return boolType } } right := fixGoTypesExpr(fn, x.Right, nil) if isSliceOrArray(x.Left.XType) && isSliceOrArray(x.Right.XType) { x.Left = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Left}}} x.Right = &cc.Expr{Op: cc.Minus, Left: &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: "cap"}, List: []*cc.Expr{x.Right}}} return boolType } fixBinary(fn, x, left, right, nil) return boolType case cc.Index, cc.Indir: left := fixGoTypesExpr(fn, x.Left, nil) if x.Right != nil { fixGoTypesExpr(fn, x.Right, nil) } if left == nil { return nil } if isSliceOrString(left) && x.Op == cc.Indir { x.Op = cc.Index x.Right = &cc.Expr{Op: cc.Number, Text: "0"} } switch left.Kind { case cc.Ptr, Slice, cc.Array: if x.Op == cc.Indir && left.Kind == cc.Ptr && left.Base.Kind == cc.Func { *x = *x.Left } return left.Base case String: return byteType } return nil case cc.Lsh, cc.Rsh: left := fixGoTypesExpr(fn, x.Left, targ) if left != nil && targ != nil && Int8 <= left.Kind && left.Kind <= Float64 && targ.Kind > left.Kind { forceConvert(fn, x.Left, left, targ) left = targ } fixShiftCount(fn, x.Right) return left case cc.LshEq, cc.RshEq: left := fixGoTypesExpr(fn, x.Left, nil) fixShiftCount(fn, x.Right) return left case cc.Name: if x.Text == "nelem" { } switch x.Text { case "T", "S", "N", "L", "P", "C": x.Text = "nil" x.XDecl = nil return nil case "nelem": x.Text = "len" x.XDecl = nil fallthrough case "len": return &cc.Type{Kind: cc.Func, Base: intType} } if x.XDecl == nil { if x.Text == "true" || x.Text == "false" { return x.XType } return nil } return x.XDecl.Type case cc.Number: return idealType case cc.Minus, cc.Plus, cc.Twid: return fixGoTypesExpr(fn, x.Left, targ) case cc.Offsetof: // TODO return nil case cc.Paren: return fixGoTypesExpr(fn, x.Left, targ) case cc.PostDec, cc.PostInc: left := fixGoTypesExpr(fn, x.Left, nil) if x.Op == cc.PostInc && isSliceOrString(left) { old := copyExpr(x.Left) x.Op = cc.Eq x.Right = &cc.Expr{Op: ExprSlice, List: []*cc.Expr{old, &cc.Expr{Op: cc.Number, Text: "1"}, nil}} } return nil case cc.SizeofExpr: left := fixGoTypesExpr(fn, x.Left, nil) if left != nil && (left.Kind == cc.Array || left.Kind == Slice) && left.Base.Def().Is(Uint8) { x.Op = cc.Call x.List = []*cc.Expr{x.Left} x.Left = &cc.Expr{Op: cc.Name, Text: "len"} return intType } return nil case cc.SizeofType: return nil case cc.String: return &cc.Type{Kind: String} case cc.VaArg: // TODO return nil } }
func negate(x *cc.Expr) *cc.Expr { switch x.Op { case cc.Paren: return negate(x.Left) case cc.OrOr: x.Op = cc.AndAnd x.Left = negate(x.Left) x.Right = negate(x.Right) case cc.AndAnd: x.Op = cc.OrOr x.Left = negate(x.Left) x.Right = negate(x.Right) case cc.EqEq: x.Op = cc.NotEq case cc.NotEq: x.Op = cc.EqEq case cc.Lt: x.Op = cc.GtEq case cc.GtEq: x.Op = cc.Lt case cc.Gt: x.Op = cc.LtEq case cc.LtEq: x.Op = cc.Gt default: x = &cc.Expr{Op: cc.Not, Left: x} } return x }
func fixSpecialCompare(fn *cc.Decl, x *cc.Expr) bool { if (x.Right.Op != cc.Number || x.Right.Text != "0") && x.Right.String() != "nil" && x.Right.String() != `""` || x.Left.Op != cc.Call || x.Left.Left.Op != cc.Name { return false } call := x.Left switch call.Left.Text { case "memcmp": if len(call.List) != 3 { // fprintf(x.Span, "unsupported %v", x) return false } obj1, obj1Type := objIndir(fn, call.List[0]) obj2, obj2Type := objIndir(fn, call.List[1]) if obj1Type == nil || !sameType(obj1Type, obj2Type) { // fprintf(x.Span, "unsupported %v", call) return true } if !matchSize(fn, obj1, obj1Type, call.List[2]) && !matchSize(fn, obj2, obj2Type, call.List[2]) { // fprintf(x.Span, "unsupported %v - wrong size", call) return true } x.Left = obj1 x.Right = obj2 x.List = nil x.XType = boolType return true case "strncmp": if len(call.List) != 3 { fprintf(x.Span, "unsupported %v", x) return false } call.Left = &cc.Expr{Op: cc.Name, Text: "strings.HasPrefix"} call.List = call.List[:2] call.XType = boolType if x.Op == cc.EqEq { *x = *call } else if x.Op == cc.NotEq { x.Op = cc.Not x.Right = nil } x.XType = boolType return true case "strstr": if len(call.List) != 2 { fprintf(x.Span, "unsupported %v", x) return false } call.Left = &cc.Expr{Op: cc.Name, Text: "strings.Contains"} call.XType = boolType if x.Op == cc.NotEq { *x = *call } else if x.Op == cc.EqEq { x.Op = cc.Not x.Right = nil } x.XType = boolType return true case "utfrune": if len(call.List) != 2 { fprintf(x.Span, "unsupported %v", x) return false } call.Left = &cc.Expr{Op: cc.Name, Text: "strings.ContainsRune"} call.XType = boolType if x.Op == cc.NotEq { *x = *call } else if x.Op == cc.EqEq { x.Op = cc.Not x.Right = nil } x.XType = boolType return true case "ucistrcmp": if len(call.List) != 2 { fprintf(x.Span, "unsupported %v", x) return false } call.Left = &cc.Expr{Op: cc.Name, Text: "strings.EqualFold"} call.XType = boolType if x.Op == cc.EqEq { *x = *call } else if x.Op == cc.NotEq { x.Op = cc.Not x.Right = nil } x.XType = boolType return true case "strcmp": if len(call.List) != 2 { fprintf(x.Span, "unsupported %v", x) return false } obj1 := call.List[0] obj2 := call.List[1] x.Left = obj1 x.Right = obj2 x.List = nil x.XType = boolType return true case "isspacerune": if len(call.List) != 1 { fprintf(x.Span, "unsupported %v", x) return false } call.Left.Text = "unicode.IsSpace" call.Left.XDecl = nil forceConvert(fn, call.List[0], call.List[0].XType, runeType) if x.Op == cc.NotEq { *x = *call } else if x.Op == cc.EqEq { x.Op = cc.Not x.Right = nil } x.XType = boolType return true } return false }
// fixArrays rewrites uses of the untyped "Array" container defined in cmd/gc // to use native Go slices. // It has nothing to do with standard C arrays. func fixArray(fn *cc.Decl, x *cc.Expr) { // arraynew(n, sizeof(T)) becomes Go make([]T, 0, n). if isCall(x, "arraynew") { if len(x.List) != 2 { fprintf(x.Span, "wrong number of arguments to arraynew") return } if x.List[1].Op != cc.SizeofType { fprintf(x.Span, "second argument to arraynew must be sizeof(T)") return } x.Left.Text = "make" x.Left.XDecl = nil typ := &cc.Type{Kind: Slice, Base: x.List[1].Type} x.XType = typ x.List = append(x.List, x.List[0]) x.List[1] = &cc.Expr{Op: cc.Number, Text: "0"} x.List[0] = &cc.Expr{Op: ExprType, Type: typ} return } // arraylength(x) becomes len(x) if isCall(x, "arraylength") { x.Left.Text = "len" x.Left.XDecl = nil return } // arrayset is unused in practice! // arrayadd(x, &elem) becomes x = append(x, elem). // Strictly speaking, this is not a complete translation, // because in the C code x might be a pointer taken from // another place, and arrayadd changes the len at that // other place too. In cmd/gc this does not happen. if isCall(x, "arrayadd") { if len(x.List) != 2 { fprintf(x.Span, "wrong number of arguments to arrayadd") return } if x.List[1].Op != cc.Addr { fprintf(x.Span, "second argument to arrayadd must be &x, have %v", x.List[1]) return } append := copyExpr(x) append.Left.Text = "append" append.Left.XDecl = nil x.Op = cc.Eq x.Left = append.List[0] x.Right = append append.List[1] = append.List[1].Left return } // *(T**)(arrayget(x, i)) turns into x[i]. // Record that x should have translated type T*. if x.Op == cc.Indir && x.Left.Op == cc.Cast && x.Left.Type.Kind == cc.Ptr && x.Left.Type.Base.Kind == cc.Ptr && isCall(x.Left.Left, "arrayget") { call := x.Left.Left x.Op = cc.Index x.XType = x.Left.Type.Base x.Left = call.List[0] x.Right = call.List[1] saveSliceType(x.Left, x.XType) return } // TODO: arraysort }
func fixQsortCmp(decl *cc.Decl) (*cc.Type, *cc.Decl) { ftyp := decl.Type if ftyp.Kind != cc.Func || len(ftyp.Decls) != 2 || !isEmptyInterface(ftyp.Decls[0].Type) || !isEmptyInterface(ftyp.Decls[1].Type) { fprintf(decl.Span, "invalid qsort cmp function %v - wrong args", GoString(ftyp)) return nil, nil } a1, a2 := ftyp.Decls[0], ftyp.Decls[1] var eq1, eq2, p1, p2 *cc.Expr var indir1, indir2 bool cc.Preorder(decl.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Expr: if x.Op != cc.Eq { return } r := x.Right if r.Op == cc.Indir { r = r.Left } if (r.Op == TypeAssert || r.Op == cc.Cast) && r.Left.Op == cc.Name { if r.Left.XDecl == a1 && p1 == nil { p1 = x.Left eq1 = x indir1 = r != x.Right } if r.Left.XDecl == a2 && p2 == nil { p2 = x.Left eq2 = x indir2 = r != x.Right } } } }) if p1 == nil || p2 == nil { fprintf(decl.Span, "invalid qsort cmp function - cannot find arg extraction") return nil, nil } if !sameType(p1.XType, p2.XType) { fprintf(decl.Span, "invalid qsort cmp function - different arg types %v and %v", GoString(p1.XType), GoString(p2.XType)) return nil, nil } if indir1 != indir2 { fprintf(decl.Span, "invalid qsort cmp function - different arg indirection") return nil, nil } typ := p1.XType if !indir1 { if typ.Def().Kind != cc.Ptr { fprintf(decl.Span, "invalid qsort cmp function - arg ptr cast to non-ptr %v", GoString(typ)) return nil, nil } typ = typ.Def().Base } // Have all the information. Committed. // Rewrite to take x, i, j, use x[i] for p1, x[j] for p2, // take address of x[i], x[j] if there was no indirect, // replace all return z with return z < 0. newDecl := *decl decl.Body = nil decl = &newDecl cmp := decl.Name decl.Name = "(x " + cmp + ") Less" decl.Type = &cc.Type{ Kind: cc.Func, Base: boolType, Decls: []*cc.Decl{ {Name: "i", Type: &cc.Type{Kind: cc.TypedefType}}, {Name: "j", Type: intType}, }, } prefix := "" if !indir1 { prefix = "&" } eq1.Right = &cc.Expr{Op: cc.Name, Text: prefix + "x[i]", XType: p1.XType} eq2.Right = &cc.Expr{Op: cc.Name, Text: prefix + "x[j]", XType: p1.XType} cc.Preorder(decl.Body, func(x cc.Syntax) { switch x := x.(type) { case *cc.Stmt: if x.Op == cc.Return && x.Expr != nil { ret := x.Expr // Pick off 0, -1, +1. // Otherwise rewrite ret to ret < 0. switch ret.Op { case cc.Minus, cc.Plus: if ret.Left.Op == cc.Number && ret.Left.Text == "1" { if ret.Op == cc.Plus { ret.Text = "false" } else { ret.Text = "true" } ret.Op = cc.Name ret.Left = nil ret.XType = boolType return } case cc.Number: if ret.Text == "0" { ret.Op = cc.Name ret.Text = "false" ret.XType = boolType return } } x.Expr = &cc.Expr{Op: cc.Lt, Left: ret, Right: &cc.Expr{Op: cc.Number, Text: "0"}, XType: boolType} return } } }) return typ, decl }
func fixQsort(prog *cc.Prog, x *cc.Expr) { if len(x.List) != 4 { fprintf(x.Span, "unsupported %v - wrong arg count", x) return } if x.List[2].Op != cc.SizeofExpr || unparen(x.List[2].Left).String() != unparen(x.List[0]).String()+"[0]" { fprintf(x.Span, "unsupported %v - wrong elem size %v vs %v", x, unparen(x.List[2].Left).String(), unparen(x.List[0]).String()+"[0]") return } if x.List[3].Op != cc.Name || x.List[3].XDecl == nil { fprintf(x.Span, "unsupported %v - unknown comparison function", x) return } cmp := x.List[3].XDecl.Name decl := x.List[3].XDecl typ, newDecl := fixQsortCmp(decl) if typ == nil { return } x.Left.Text = "sort.Sort" x.Left.XDecl = nil x.List = []*cc.Expr{ &cc.Expr{ Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: cmp}, List: []*cc.Expr{ { Op: ExprSlice, List: []*cc.Expr{ x.List[0], nil, x.List[1], }, }, }, }, } typeDecl := &cc.Decl{ GoPackage: decl.GoPackage, SyntaxInfo: cc.SyntaxInfo{Span: decl.Span}, Name: cmp, Storage: cc.Typedef, Type: &cc.Type{ Kind: Slice, Base: typ, }, } lenFunc := &cc.Decl{ GoPackage: decl.GoPackage, SyntaxInfo: cc.SyntaxInfo{Span: decl.Span}, Name: "(x " + cmp + ") Len", Type: &cc.Type{ Kind: cc.Func, Base: intType, }, Body: &cc.Stmt{ Op: cc.Block, Block: []*cc.Stmt{ { Op: cc.Return, Expr: &cc.Expr{ Op: cc.Name, Text: "len(x)", }, }, }, }, } swapFunc := &cc.Decl{ GoPackage: decl.GoPackage, SyntaxInfo: cc.SyntaxInfo{Span: decl.Span}, Name: "(x " + cmp + ") Swap", Type: &cc.Type{ Kind: cc.Func, Base: &cc.Type{Kind: cc.Void}, Decls: []*cc.Decl{ {Name: "i", Type: &cc.Type{Kind: cc.TypedefType}}, {Name: "j", Type: intType}, }, }, Body: &cc.Stmt{ Op: cc.Block, Block: []*cc.Stmt{ { Op: cc.StmtExpr, Expr: &cc.Expr{ Op: cc.Name, Text: "x[i], x[j] = x[j], x[i]", }, }, }, }, } for i, d := range prog.Decls { if d == decl { prog.Decls = append(append(prog.Decls[:i:i], typeDecl, lenFunc, swapFunc, newDecl), prog.Decls[i+1:]...) return } } prog.Decls = append(prog.Decls[:len(prog.Decls):len(prog.Decls)], typeDecl, lenFunc, swapFunc, newDecl) }
func fixPrintf(curfn *cc.Decl, x *cc.Expr) bool { if x.Op != cc.Call { return false } if tryPrintf(curfn, x, "sprint", 1, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[1:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.Eq // sprint(strchr(s, 0), "hello") => s += "hello" if targ.Op == cc.Call && len(targ.List) == 2 && targ.Left.Text == "strchr" && targ.List[1].Text == "0" { x.Left = targ.List[0] x.Op = cc.AddEq } else if targ.Op == cc.Add && targ.Right.Op == cc.Call && targ.Right.Left.Text == "strlen" && len(targ.Right.List) == 1 && GoString(targ.Left) == GoString(targ.Right.List[0]) { x.Left = targ.Left x.Op = cc.AddEq } return true } if tryPrintf(curfn, x, "snprint", 2, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[2:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.Eq return true } if tryPrintf(curfn, x, "fmtprint", 1, "fmt.Sprintf") { targ := x.List[0] x.List = x.List[1:] x.Right = copyExpr(x) x.List = nil x.Left = targ x.Op = cc.AddEq if x.Left.Op == cc.Addr { x.Left = x.Left.Left } return true } if tryPrintf(curfn, x, "smprint", 0, "fmt.Sprintf") { x.XType = stringType return true } if tryPrintf(curfn, x, "print", 0, "fmt.Printf") { return true } if tryPrintf(curfn, x, "fprint", 1, "fmt.Fprintf") { if x.List[0].String() == "2" { x.List[0] = &cc.Expr{Op: cc.Name, Text: "os.Stderr"} } return true } if tryPrintf(curfn, x, "sysfatal", 0, "log.Fatalf") { return true } if tryPrintf(curfn, x, "fatal", 0, "") { return true } if tryPrintf(curfn, x, "ctxt->diag", 0, "") { return true } if tryPrintf(curfn, x, "diag", 0, "") { return true } if tryPrintf(curfn, x, "werrstr", 0, "") { return true } if tryPrintf(curfn, x, "yyerror", 0, "") { return true } if tryPrintf(curfn, x, "yyerrorl", 1, "") { forceConvert(curfn, x.List[0], x.List[0].XType, intType) return true } if tryPrintf(curfn, x, "onearg", 1, "") { return true } if tryPrintf(curfn, x, "warn", 0, "") { return true } if tryPrintf(curfn, x, "warnl", 1, "") { forceConvert(curfn, x.List[0], x.List[0].XType, intType) return true } if tryPrintf(curfn, x, "Bprint", 1, "fmt.Fprintf") { return true } if isCall(x, "exprfmt") && len(x.List) == 3 { // exprfmt(fp, x, y) becomes fp += exprfmt(x, y) x.Op = cc.AddEq x.Right = &cc.Expr{Op: cc.Call, Left: x.Left, List: x.List[1:]} x.Left = x.List[0] x.List = nil x.XType = stringType return true } if isCall(x, "fmtstrinit") && len(x.List) == 1 && x.List[0].Op == cc.Addr { x.Op = cc.Eq x.Left = x.List[0].Left x.List = nil x.Right = &cc.Expr{Op: cc.Name, Text: `""`} } if isCall(x, "fmtstrflush") && len(x.List) == 1 && x.List[0].Op == cc.Addr { x.Op = cc.Paren x.Left = x.List[0].Left x.List = nil } return false }
func doSideEffects(x *cc.Expr, before, after *[]*cc.Stmt, mode int) { if x == nil { return } // Cannot hoist side effects from conditionally evaluated expressions // into unconditionally evaluated statement lists. // For now, detect but do not handle. switch x.Op { case cc.Cond: doSideEffects(x.List[0], before, after, mode&^sideStmt|sideNoAfter) checkNoSideEffects(x.List[1], 0) checkNoSideEffects(x.List[2], 0) case cc.AndAnd, cc.OrOr: doSideEffects(x.Left, before, after, mode&^sideStmt|sideNoAfter) checkNoSideEffects(x.Right, 0) case cc.Comma: var leftover []*cc.Expr for i, y := range x.List { m := mode | sideNoAfter if i+1 < len(x.List) { m |= sideStmt } doSideEffects(y, before, after, m) switch y.Op { case cc.PostInc, cc.PostDec, cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: y}) default: leftover = append(leftover, y) } } x.List = leftover default: doSideEffects(x.Left, before, after, mode&^sideStmt) doSideEffects(x.Right, before, after, mode&^sideStmt) for _, y := range x.List { doSideEffects(y, before, after, mode&^sideStmt) } } if mode&sideStmt != 0 { // Expression as statement. // Can leave x++ alone, can rewrite ++x to x++, can leave x [op]= y alone. switch x.Op { case cc.PreInc: x.Op = cc.PostInc return case cc.PreDec: x.Op = cc.PostDec return case cc.PostInc, cc.PostDec: return case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: return case cc.Call: return } } switch x.Op { case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: x.Left = forceCheap(before, x.Left) old := copyExpr(x) *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) fixMerge(x, x.Left) case cc.PreInc, cc.PreDec: x.Left = forceCheap(before, x.Left) old := copyExpr(x) old.SyntaxInfo = cc.SyntaxInfo{} if old.Op == cc.PreInc { old.Op = cc.PostInc } else { old.Op = cc.PostDec } *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) fixMerge(x, x.Left) case cc.PostInc, cc.PostDec: x.Left = forceCheap(before, x.Left) if mode&sideNoAfter != 0 { // Not allowed to generate fixups afterward. d := &cc.Decl{ Name: fmt.Sprintf("tmp%d", <-tmpGen), Type: x.Left.XType, } eq := &cc.Expr{ Op: ColonEq, Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, Right: x.Left, } old := copyExpr(x.Left) old.SyntaxInfo = cc.SyntaxInfo{} *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: eq}, &cc.Stmt{Op: cc.StmtExpr, Expr: &cc.Expr{Op: x.Op, Left: old}}, ) x.Op = cc.Name x.Text = d.Name x.XDecl = d x.Left = nil break } old := copyExpr(x) old.SyntaxInfo = cc.SyntaxInfo{} *after = append(*after, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) fixMerge(x, x.Left) case cc.Cond: // Rewrite c ? y : z into tmp with initialization: // var tmp typeof(c?y:z) // if c { // tmp = y // } else { // tmp = z // } d := &cc.Decl{ Name: "tmp", Type: x.XType, } *before = append(*before, &cc.Stmt{Op: cc.StmtDecl, Decl: d}, &cc.Stmt{Op: cc.If, Expr: x.List[0], Body: &cc.Stmt{ Op: cc.StmtExpr, Expr: &cc.Expr{ Op: cc.Eq, Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, Right: x.List[1], }, }, Else: &cc.Stmt{ Op: cc.StmtExpr, Expr: &cc.Expr{ Op: cc.Eq, Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, Right: x.List[2], }, }, }, ) x.Op = cc.Name x.Text = d.Name x.XDecl = d x.List = nil case cc.Call: if x.Left.Text == "fmtstrcpy" || x.Left.Text == "fmtprint" { old := copyExpr(x) old.SyntaxInfo = cc.SyntaxInfo{} *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) x.Op = cc.Number x.Text = "0" x.XDecl = nil x.Left = nil x.List = nil } } }