Exemple #1
0
func exportDecls(cfg *Config, prog *cc.Prog) {
	for _, d := range cfg.topDecls {
		if shouldExport(cfg, d.Name) {
			exportDecl(d)
		}
		pkg := d.GoPackage
		if pkg == "" {
			continue
		}
		cc.Preorder(d, func(x cc.Syntax) {
			switch x := x.(type) {
			case *cc.Expr:
				if x.Op == cc.Name && x.XDecl != nil && x.XDecl.GoPackage != "" && x.XDecl.GoPackage != pkg {
					exportDecl(x.XDecl)
				}
			case *cc.Type:
				if x.Kind == cc.TypedefType && x.TypeDecl != nil && x.TypeDecl.GoPackage != "" && x.TypeDecl.GoPackage != pkg {
					exportDecl(x.TypeDecl)
					x.Name = x.TypeDecl.Name
				}
			}
		})
	}

	for _, d := range cfg.topDecls {
		renameDecl(cfg, d)
	}
}
Exemple #2
0
func fixBools(prog *cc.Prog) {
	cc.Preorder(prog, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Expr:
			if x.Op == cc.Not {
				switch x.Left.Op {
				case cc.OrOr, cc.AndAnd, cc.EqEq, cc.NotEq, cc.LtEq, cc.GtEq, cc.Lt, cc.Gt, cc.Paren:
					x.Left = negate(x.Left)
					fixMerge(x, x.Left)
				}
			}
		}
	})
}
Exemple #3
0
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
}
Exemple #4
0
func fixFormatter(fn *cc.Decl) {
	// Find va_arg assignment.
	var arg *cc.Expr
	var argType *cc.Type
	var ps []*cc.Expr
	cc.Preorder(fn.Body, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Expr:
			if x.Op == cc.Name && strings.HasPrefix(x.Text, "bigP") {
				ps = append(ps, x)
			}
		case *cc.Stmt:
			stmt := x
			if stmt.Op != cc.StmtExpr {
				return
			}
			expr := stmt.Expr
			if expr.Op != cc.Eq {
				return
			}
			if expr.Left.Op == cc.Name && strings.HasPrefix(expr.Left.Text, "bigP") {
				stmt.Op = cc.Empty
				stmt.Expr = nil
				return
			}
			if expr.Op != cc.Eq || expr.Right.Op != cc.VaArg {
				return
			}
			if arg != nil {
				fprintf(fn.Span, "multiple va_arg in formatter")
			}
			arg = expr.Left
			argType = expr.Right.Type
			stmt.Op = cc.Empty
		}
	})

	fp := fn.Type.Decls[0]
	fp.Type = stringType
	fn.Type.Base = stringType
	if arg != nil {
		fn.Type.Decls[0] = arg.XDecl
	} else {
		if len(fn.Type.Decls) == 1 {
			fprintf(fn.Span, "missing va_arg in formatter")
			return
		}
		fn.Type.Decls = fn.Type.Decls[1:]
		decl := &cc.Stmt{
			Op:   cc.StmtDecl,
			Decl: fp,
		}
		fn.Body.Block = append([]*cc.Stmt{decl}, fn.Body.Block...)
	}

	if strings.HasPrefix(fn.Name, "Dconv") && len(ps) > 0 {
		pd := &cc.Decl{Name: "p", Type: ps[0].XDecl.Type}
		fd := &cc.Decl{Name: "flag", Type: intType}
		for _, p := range ps {
			p.XDecl = pd
		}
		fn.Type.Decls = []*cc.Decl{
			pd,
			fd,
			arg.XDecl,
		}
	}
	if len(fn.Name) == 5 && strings.HasSuffix(fn.Name, "conv") {
		switch fn.Name[0] {
		case 'B', 'E', 'F', 'H', 'J', 'N', 'O', 'Q', 'S', 'T', 'V', 'Z':
			fn.Type.Decls = append(fn.Type.Decls, &cc.Decl{
				Name: "flag",
				Type: intType,
			})
		}
	}

	cc.Preorder(fn.Body, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Stmt:
			if arg != nil && x.Op == cc.StmtDecl && x.Decl == arg.XDecl {
				x.Decl = fp
			}

			if x.Op == cc.Return && x.Expr != nil && x.Expr.Text == "0" {
				x.Expr = &cc.Expr{Op: cc.Name, Text: fp.Name, XDecl: fp}
			}

		case *cc.Expr:
			if x.Op == cc.Arrow && x.Text == "flags" {
				x.Op = cc.Name
				x.Text = "flag"
				x.XDecl = nil
				x.Left = nil
			}
		}
	})

}
Exemple #5
0
func fixPrintFormat(curfn *cc.Decl, fx *cc.Expr, args []*cc.Expr) []*cc.Expr {
	for _, arg := range args {
		fixGoTypesExpr(curfn, arg, nil)
		cc.Preorder(arg, func(x cc.Syntax) {
			if x, ok := x.(*cc.Expr); ok && x.Op == cc.Name && strings.HasPrefix(x.Text, "bigP") {
				x.Text = "p"
				x.XDecl = nil
			}
		})
	}

	isGC := strings.Contains(fx.Span.Start.File, "cmd/gc")
	isCompiler := isGC || strings.Contains(fx.Span.Start.File, "cmd/6g") || strings.Contains(fx.Span.Start.File, "cmd/8g") || strings.Contains(fx.Span.Start.File, "cmd/5g") || strings.Contains(fx.Span.Start.File, "cmd/9g")

	narg := 0
	for j, text := range fx.Texts {
		format, err := strconv.Unquote(text)
		if err != nil {
			fprintf(fx.Span, "cannot parse quoted string: %v", err)
			return args
		}

		suffix := ""

		var buf bytes.Buffer
		start := 0
		for i := 0; i < len(format); i++ {
			if format[i] != '%' {
				continue
			}
			buf.WriteString(format[start:i])
			start = i
			i++
			if i < len(format) && format[i] == '%' {
				buf.WriteByte('%')
				buf.WriteByte('%')
				start = i + 1
				continue
			}
			for i < len(format) {
				c := format[i]
				switch c {
				case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '#', '-', '.', ',', ' ', 'h', 'l', 'u', '*':
					i++
					continue
				}
				break
			}
			if i >= len(format) {
				fprintf(fx.Span, "print format ends mid-verb")
				return args
			}
			flags, verb := format[start:i], format[i]
			start = i + 1

			allFlags := flags
			_ = allFlags

			flags = strings.Replace(flags, "h", "", -1)
			flags = strings.Replace(flags, "l", "", -1)
			flags = strings.Replace(flags, "u", "", -1)

			if j := strings.Index(flags, "#0"); j >= 0 && verb == 'x' {
				k := j + 2
				for k < len(flags) && '0' <= flags[k] && flags[k] <= '9' {
					k++
				}
				n, _ := strconv.Atoi(flags[j+2 : k])
				flags = flags[:j+2] + fmt.Sprint(n-2) + flags[k:]
			}

			narg += strings.Count(allFlags, "*")

			convert := ""
			switch verb {
			default:
				fprintf(fx.Span, "unrecognized format %s%c", flags, verb)
				buf.WriteString("%")
				buf.WriteString(flags)
				buf.WriteString(string(verb))

			case 'f', 'e', 'g', 's', 'c', 'p':
				// usual meanings
				buf.WriteString(flags)
				buf.WriteString(string(verb))

			case 'x', 'X', 'o', 'd', 'b':
				// usual meanings, but force unsigned if u is given
				buf.WriteString(flags)
				buf.WriteString(string(verb))
				if narg >= len(args) || !strings.Contains(allFlags, "u") {
					break
				}
				arg := args[narg]
				if t := arg.XType.Def(); t != nil {
					switch t.Kind {
					case Int8:
						convert = "uint8"
					case Int16:
						convert = "uint16"
					case Int32:
						convert = "uint32"
					case Int64:
						convert = "uint64"
					case Int:
						convert = "uint"
					}
				}

			case 'C': // rune
				buf.WriteString(flags)
				buf.WriteString("c")

			case 'q': // plan 9 rc(1) quoted string
				buf.WriteString(flags)
				buf.WriteString("v")
				convert = "plan9quote"

			case 'A': // asm opcode
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				if narg < len(args) {
					forceConvert(nil, args[narg], args[narg].XType, intType)
				}
				convert = "Aconv" + suffix
				if isCompiler {
					switch {
					case strings.Contains(fx.Span.Start.File, "cmd/6g"):
						convert = "amd64." + convert
					case strings.Contains(fx.Span.Start.File, "cmd/8g"):
						convert = "i386." + convert
					case strings.Contains(fx.Span.Start.File, "cmd/5g"):
						convert = "arm." + convert
					case strings.Contains(fx.Span.Start.File, "cmd/9g"):
						convert = "ppc64." + convert
					}
				}

			case 'L':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				if narg >= len(args) {
					break
				}
				arg := args[narg]
				if (arg.Op == cc.Dot || arg.Op == cc.Arrow) && arg.Text == "lineno" {
					arg.Text = "Line"
					arg.XDecl = nil
					args[narg] = &cc.Expr{Op: cc.Call, Left: arg, XType: stringType}
					break
				}
				convert = "ctxt.Line"
				if isCompiler {
					if isGC {
						convert = "Ctxt.Line"
					} else {
						convert = "gc.Ctxt.Line"
					}
					forceConvert(curfn, arg, arg.XType, intType)
				}

			case '@':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				convert = "RAconv" + suffix

			case '^':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				convert = "DRconv" + suffix

			case 'D':
				if allFlags != "%" && allFlags != "%l" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				if narg >= len(args) {
					break
				}
				flag := &cc.Expr{Op: cc.Name, Text: "0"}
				if strings.Contains(allFlags, "l") {
					flag.Text = "obj.FmtLong"
				}
				arg := args[narg]
				args[narg] = &cc.Expr{
					Op:   cc.Call,
					Left: &cc.Expr{Op: cc.Name, Text: "Dconv" + suffix},
					List: []*cc.Expr{
						&cc.Expr{Op: cc.Name, Text: "p"},
						flag,
						arg,
					},
					XType: stringType,
				}
				if isCompiler {
					args[narg].List = args[narg].List[2:]
					args[narg].Left.Text = "Ctxt.Dconv"
					if !isGC {
						args[narg].Left.Text = "gc.Ctxt.Dconv"
					}
				}

			case 'M':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				convert = "Mconv" + suffix

			case 'R':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				forceConvert(curfn, args[narg], args[narg].XType, intType)
				convert = "Rconv" + suffix
				if isCompiler {
					convert = "Ctxt." + convert
					if !isGC {
						convert = "gc." + convert
					}
				}

			case '$':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%q")

			case 'P':
				if allFlags != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")

			case 'r': // plan 9 errstr
				buf.WriteString("%v")
				if narg > len(args) {
					break
				}
				args = append(append(args[:narg:narg], &cc.Expr{Op: cc.Name, Text: "err"}), args[narg:]...)

			case 'B', 'E', 'F', 'H', 'J', 'N', 'O', 'Q', 'S', 'T', 'V', 'Z':
				switch verb {
				case 'E', 'O':
					convert = "int"
				}
				f := allFlags
				mod := "0"
				if strings.Contains(f, "-") {
					mod += "|obj.FmtLeft"
					f = strings.Replace(f, "-", "", 1)
				}
				if strings.Contains(f, "h") {
					mod += "|obj.FmtShort"
					f = strings.Replace(f, "h", "", 1)
					if strings.Contains(f, "h") {
						mod += "|obj.FmtByte"
						f = strings.Replace(f, "h", "", 1)
					}
				}
				if strings.Contains(f, "#") {
					mod += "|obj.FmtSharp"
					f = strings.Replace(f, "#", "", 1)
				}
				if strings.Contains(f, "l") {
					mod += "|obj.FmtLong"
					f = strings.Replace(f, "l", "", 1)
				}
				if strings.Contains(f, ",") {
					mod += "|obj.FmtComma"
					f = strings.Replace(f, ",", "", 1)
				}
				if strings.Contains(f, "+") {
					mod += "|obj.FmtSign"
					f = strings.Replace(f, "+", "", 1)
				}
				if strings.Contains(f, "u") {
					mod += "|obj.FmtUnsigned"
					f = strings.Replace(f, "u", "", 1)
				}
				if f != "%" {
					fprintf(fx.Span, "format %s%c", allFlags, verb)
				}
				buf.WriteString("%v")
				if narg >= len(args) {
					break
				}
				if mod != "0" {
					mod = mod[2:]
				}
				if !isCompiler {
					mod = strings.Replace(mod, "obj.", "", -1)
				}
				flag := &cc.Expr{Op: cc.Name, Text: mod}
				if convert != "" {
					args[narg] = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: convert}, List: []*cc.Expr{args[narg]}, XType: stringType}
					convert = ""
				}
				arg := args[narg]
				args[narg] = &cc.Expr{
					Op:   cc.Call,
					Left: &cc.Expr{Op: cc.Name, Text: string(verb) + "conv"},
					List: []*cc.Expr{
						arg,
						flag,
					},
					XType: stringType,
				}
				if !isGC {
					args[narg].Left.Text = "gc." + args[narg].Left.Text
				}
			}

			if convert != "" && narg < len(args) {
				arg := args[narg]
				args[narg] = &cc.Expr{Op: cc.Call, Left: &cc.Expr{Op: cc.Name, Text: convert}, List: []*cc.Expr{arg}, XType: stringType}
			}

			narg++
		}
		buf.WriteString(format[start:])
		fx.Texts[j] = strconv.Quote(buf.String())
	}

	return args
}
Exemple #6
0
// Apply DeMorgan's law and invert comparisons
// to simplify negation of boolean expressions.
func simplifyBool(cfg *Config, prog *cc.Prog) {
	cc.Preorder(prog, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Expr:
			switch x.Op {
			case cc.Not:
				y := x.Left
				for y.Op == cc.Paren {
					y = y.Left
				}
				switch y.Op {
				case cc.AndAnd:
					*x = *y
					x.Left = &cc.Expr{Op: cc.Not, Left: x.Left}
					x.Right = &cc.Expr{Op: cc.Not, Left: x.Right}
					x.Op = cc.OrOr

				case cc.OrOr:
					*x = *y
					x.Left = &cc.Expr{Op: cc.Not, Left: x.Left}
					x.Right = &cc.Expr{Op: cc.Not, Left: x.Right}
					x.Op = cc.AndAnd

				case cc.EqEq:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.NotEq

				case cc.NotEq:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.EqEq

				case cc.Lt:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.GtEq

				case cc.LtEq:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.Gt

				case cc.Gt:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.LtEq

				case cc.GtEq:
					if isfloat(x.Left.XType) {
						break
					}
					*x = *y
					x.Op = cc.Lt
				}
			}
		}
	})
}
Exemple #7
0
// Rewrite from C constructs to Go constructs.
func rewriteSyntax(cfg *Config, prog *cc.Prog) {
	numRewrite++
	cc.Preorder(prog, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Stmt:
			rewriteStmt(x)

		case *cc.Expr:
			switch x.Op {
			case cc.Name:
				switch x.Text {
				case "nil":
					x.XDecl = nil // just nil, not main.Nil
				case "nelem":
					x.Text = "len"
					x.XDecl = nil
				}
			case cc.Number:
				// Rewrite char literal.
				// In general we'd need to rewrite all string and char literals
				// but these are the only forms that comes up.
				switch x.Text {
				case `'\0'`:
					x.Text = `'\x00'`
				case `'\"'`:
					x.Text = `'"'`
				}

			case cc.Paren:
				switch x.Left.Op {
				case cc.Number, cc.Name:
					fixMerge(x, x.Left)
				}

			case cc.OrEq, cc.AndEq, cc.Or, cc.Eq, cc.EqEq, cc.NotEq, cc.LtEq, cc.GtEq, cc.Lt, cc.Gt:
				cutParen(x, cc.Or, cc.And, cc.Lsh, cc.Rsh)
			}

		case *cc.Type:
			// Rewrite int f(void) to int f().
			if x.Kind == cc.Func && len(x.Decls) == 1 && x.Decls[0].Name == "" && x.Decls[0].Type.Is(cc.Void) {
				x.Decls = nil
			}
		}
	})

	// Apply changed struct tags to typedefs.
	// Excise dead pieces.
	cc.Postorder(prog, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Type:
			if x.Kind == cc.TypedefType && x.Base != nil && x.Base.Tag != "" {
				x.Name = x.Base.Tag
			}

		case *cc.Stmt:
			if x.Op == cc.StmtExpr && x.Expr.Op == cc.Comma && len(x.Expr.List) == 0 {
				x.Op = cc.Empty
			}
			x.Block = filterBlock(x.Block)

		case *cc.Expr:
			if x.Op == ExprBlock {
				x.Block = filterBlock(x.Block)
			}

			switch x.Op {
			case cc.Add, cc.Sub:
				// Turn p + y - z, which is really (p + y) - z, into p + (y - z),
				// so that there is only one pointer addition (which will turn into
				// a slice operation using y-z as the index).
				if x.XType != nil && x.XType.Kind == cc.Ptr {
					switch x.Left.Op {
					case cc.Add, cc.Sub:
						if x.Left.XType != nil && x.Left.XType.Kind == cc.Ptr {
							p, op1, y, op2, z := x.Left.Left, x.Left.Op, x.Left.Right, x.Op, x.Right
							if op1 == cc.Sub {
								y = &cc.Expr{Op: cc.Minus, Left: y, XType: y.XType}
							}
							x.Op = cc.Add
							x.Left = p
							x.Right = &cc.Expr{Op: op2, Left: y, Right: z, XType: x.XType}
						}
					}
				}
			}

			// Turn c + p - q, which is really (c + p) - q, into c + (p - q),
			// so that there is no int + ptr addition, only a ptr - ptr subtraction.
			if x.Op == cc.Sub && x.Left.Op == cc.Add && !isPtrOrArray(x.XType) && isPtrOrArray(x.Left.XType) && !isPtrOrArray(x.Left.Left.XType) {
				c, p, q := x.Left.Left, x.Left.Right, x.Right
				expr := x.Left
				expr.Left = p
				expr.Right = q
				expr.Op = cc.Sub
				x.Op = cc.Add
				x.Left = c
				x.Right = expr
				expr.XType = x.XType
			}
		}
	})
}
Exemple #8
0
// renameDecls renames file-local declarations to make them
// unique across the whole set of files being considered.
// For now, it appends the file base name to the declared name.
// Eventually it could be smarter and not do that when not necessary.
// It also renames names like 'type' and 'func' to avoid Go keywords.
func renameDecls(cfg *Config, prog *cc.Prog) {
	// Rewrite C identifiers to avoid important Go words (keywords, iota, etc).
	cc.Preorder(prog, func(x cc.Syntax) {
		switch x := x.(type) {
		case *cc.Decl:
			if goKeyword[x.Name] {
				// NOTE: Must put _ last so that name can be upper-cased for export.
				x.Name += "_"
			}

		case *cc.Stmt:
			for _, lab := range x.Labels {
				if goKeyword[lab.Name] {
					lab.Name += "_"
				}
			}
			switch x.Op {
			case cc.Goto:
				if goKeyword[x.Text] {
					x.Text += "_"
				}
			}
		}
	})

	// Assign to packages (needed below but also in writeGoFiles).
	for _, d := range prog.Decls {
		if d.Body != nil && d.Body.Span.Start.File != "" {
			d.Span = d.Body.Span
		}
		d.GoPackage = cfg.filePackage(d.Span.Start.File)
	}

	// Build list of declared top-level names.
	// Not just prog.Decls because of enums and struct definitions.
	typedefs := map[*cc.Type]bool{}
	for _, d := range prog.Decls {
		if d.Storage&cc.Typedef != 0 {
			typedefs[d.Type] = true
		}
	}

	var decls []*cc.Decl
	for _, d := range prog.Decls {
		if d.Name == "" {
			if typedefs[d.Type] {
				continue
			}
			switch d.Type.Kind {
			case cc.Struct:
				if d.Type.Tag != "" {
					decls = append(decls, d)
					d.Name = d.Type.Tag
					d.Storage = cc.Typedef
				}
				if d.Type.TypeDecl == nil {
					d.Type.TypeDecl = d
				}
			case cc.Enum:
				d.Type.Tag = "" // enum tags are worthless
				for _, dd := range d.Type.Decls {
					decls = append(decls, dd)
				}
			}
			continue
		}
		decls = append(decls, d)
		if d.Storage&cc.Typedef != 0 && d.Type != nil && d.Type.TypeDecl == nil {
			d.Type.TypeDecl = d
		}
	}

	// Assign declarations to packages and identify conflicts.
	count := make(map[string]int)
	src := make(map[string]string)
	for _, d := range decls {
		// TODO(rsc): I don't understand why this is necessary given the above.
		if d.Body != nil && d.Body.Span.Start.File != "" {
			d.Span = d.Body.Span
		}
		d.GoPackage = cfg.filePackage(d.Span.Start.File)
		key := d.GoPackage + "." + d.Name
		if count[key]++; count[key] > 1 {
			if d.Span.String() == src[key] {
				// Assume this is a nested header and ignore duplicates.
				count[key] = 1
				continue
			}
			fprintf(d.Span, "conflicting name %s in %s (last at %s)", d.Name, d.GoPackage, src[key])
			continue
		}
		src[key] = fmt.Sprintf("%s:%d", d.Span.Start.File, d.Span.Start.Line)
	}

	// Rename static, conflicting names.
	for _, d := range decls {
		key := d.GoPackage + "." + d.Name
		if count[key] > 1 {
			file := filepath.Base(d.Span.Start.File)
			if i := strings.Index(file, "."); i >= 0 {
				file = file[:i]
			}
			d.Name += "_" + file
		}

		if d.Type.Kind == cc.Func {
			if d.Body != nil {
				for _, s := range d.Body.Block {
					if s.Op == cc.StmtDecl && s.Decl.Storage&cc.Static != 0 {
						// Add function name as prefix.
						// Will print at top level.
						dd := s.Decl
						dd.Name = d.Name + "_" + dd.Name
					}
				}
			}
		}
	}

	cfg.topDecls = decls
}