Commit 95b8cbfe authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: add column details to export data

This CL updates the export data format to include column details when
writing out position data. cmd/compile is updated to generate and make
use of the new details, but go/internal/gcimporter only knows how to
read the data. It doesn't yet actually make use of it.

Experimentally across a wide range of packages, this increases export
data size by around 4%. However, it has no impact on binary size.
(Notably, it actually shrinks k8s.io/kubernetes/cmd/kubelet's binary
size by 24kB, but it's unclear to me why at this time.)

Updates #28259.

Change-Id: I351fb340839df8d3adced49b3757c4537fb91b3f
Reviewed-on: https://go-review.googlesource.com/c/go/+/196963
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 683ef8c8
......@@ -184,8 +184,9 @@
// }
//
//
// Pos encodes a file:line pair, incorporating a simple delta encoding
// scheme within a data object. See exportWriter.pos for details.
// Pos encodes a file:line:column triple, incorporating a simple delta
// encoding scheme within a data object. See exportWriter.pos for
// details.
//
//
// Compiler-specific details.
......@@ -212,8 +213,9 @@ import (
)
// Current indexed export format version. Increase with each format change.
// 1: added column details to Pos
// 0: Go1.11 encoding
const iexportVersion = 0
const iexportVersion = 1
// predeclReserved is the number of type offsets reserved for types
// implicitly declared in the universe block.
......@@ -401,10 +403,11 @@ func (p *iexporter) pushDecl(n *Node) {
type exportWriter struct {
p *iexporter
data intWriter
currPkg *types.Pkg
prevFile string
prevLine int64
data intWriter
currPkg *types.Pkg
prevFile string
prevLine int64
prevColumn int64
}
func (p *iexporter) doDecl(n *Node) {
......@@ -510,29 +513,39 @@ func (w *exportWriter) pos(pos src.XPos) {
p := Ctxt.PosTable.Pos(pos)
file := p.Base().AbsFilename()
line := int64(p.RelLine())
column := int64(p.RelCol())
// When file is the same as the last position (common case),
// we can save a few bytes by delta encoding just the line
// number.
// Encode position relative to the last position: column
// delta, then line delta, then file name. We reserve the
// bottom bit of the column and line deltas to encode whether
// the remaining fields are present.
//
// Note: Because data objects may be read out of order (or not
// at all), we can only apply delta encoding within a single
// object. This is handled implicitly by tracking prevFile and
// prevLine as fields of exportWriter.
if file == w.prevFile {
delta := line - w.prevLine
w.int64(delta)
if delta == deltaNewFile {
w.int64(-1)
// object. This is handled implicitly by tracking prevFile,
// prevLine, and prevColumn as fields of exportWriter.
deltaColumn := (column - w.prevColumn) << 1
deltaLine := (line - w.prevLine) << 1
if file != w.prevFile {
deltaLine |= 1
}
if deltaLine != 0 {
deltaColumn |= 1
}
w.int64(deltaColumn)
if deltaColumn&1 != 0 {
w.int64(deltaLine)
if deltaLine&1 != 0 {
w.string(file)
}
} else {
w.int64(deltaNewFile)
w.int64(line) // line >= 0
w.string(file)
w.prevFile = file
}
w.prevFile = file
w.prevLine = line
w.prevColumn = column
}
func (w *exportWriter) pkg(pkg *types.Pkg) {
......
......@@ -242,9 +242,10 @@ type importReader struct {
strings.Reader
p *iimporter
currPkg *types.Pkg
prevBase *src.PosBase
prevLine int64
currPkg *types.Pkg
prevBase *src.PosBase
prevLine int64
prevColumn int64
}
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
......@@ -446,16 +447,16 @@ func (r *importReader) qualifiedIdent() *types.Sym {
func (r *importReader) pos() src.XPos {
delta := r.int64()
if delta != deltaNewFile {
r.prevLine += delta
} else if l := r.int64(); l == -1 {
r.prevLine += deltaNewFile
} else {
r.prevBase = r.posBase()
r.prevLine = l
r.prevColumn += delta >> 1
if delta&1 != 0 {
delta = r.int64()
r.prevLine += delta >> 1
if delta&1 != 0 {
r.prevBase = r.posBase()
}
}
if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 {
if (r.prevBase == nil || r.prevBase.AbsFilename() == "") && r.prevLine == 0 && r.prevColumn == 0 {
// TODO(mdempsky): Remove once we reliably write
// position information for all nodes.
return src.NoXPos
......@@ -464,7 +465,7 @@ func (r *importReader) pos() src.XPos {
if r.prevBase == nil {
Fatalf("missing posbase")
}
pos := src.MakePos(r.prevBase, uint(r.prevLine), 0)
pos := src.MakePos(r.prevBase, uint(r.prevLine), uint(r.prevColumn))
return Ctxt.PosTable.XPos(pos)
}
......
......@@ -328,7 +328,7 @@ func (p *importer) pos() token.Pos {
p.prevFile = file
p.prevLine = line
return p.fake.pos(file, line)
return p.fake.pos(file, line, 0)
}
// Synthesize a token.Pos
......@@ -337,7 +337,9 @@ type fakeFileSet struct {
files map[string]*token.File
}
func (s *fakeFileSet) pos(file string, line int) token.Pos {
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
// TODO(mdempsky): Make use of column.
// Since we don't know the set of needed file positions, we
// reserve maxlines positions per file.
const maxlines = 64 * 1024
......
......@@ -61,8 +61,8 @@ const (
// If the export data version is not recognized or the format is otherwise
// compromised, an error is returned.
func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
const currentVersion = 0
version := -1
const currentVersion = 1
version := int64(-1)
defer func() {
if e := recover(); e != nil {
if version > currentVersion {
......@@ -75,9 +75,9 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
r := &intReader{bytes.NewReader(data), path}
version = int(r.uint64())
version = int64(r.uint64())
switch version {
case currentVersion:
case currentVersion, 0:
default:
errorf("unknown iexport format version %d", version)
}
......@@ -91,7 +91,8 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
r.Seek(sLen+dLen, io.SeekCurrent)
p := iimporter{
ipath: path,
ipath: path,
version: int(version),
stringData: stringData,
stringCache: make(map[uint64]string),
......@@ -169,7 +170,8 @@ func iImportData(fset *token.FileSet, imports map[string]*types.Package, data []
}
type iimporter struct {
ipath string
ipath string
version int
stringData []byte
stringCache map[uint64]string
......@@ -249,6 +251,7 @@ type importReader struct {
currPkg *types.Package
prevFile string
prevLine int64
prevColumn int64
}
func (r *importReader) obj(name string) {
......@@ -438,6 +441,19 @@ func (r *importReader) qualifiedIdent() (*types.Package, string) {
}
func (r *importReader) pos() token.Pos {
if r.p.version >= 1 {
r.posv1()
} else {
r.posv0()
}
if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
return token.NoPos
}
return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
}
func (r *importReader) posv0() {
delta := r.int64()
if delta != deltaNewFile {
r.prevLine += delta
......@@ -447,12 +463,18 @@ func (r *importReader) pos() token.Pos {
r.prevFile = r.string()
r.prevLine = l
}
}
if r.prevFile == "" && r.prevLine == 0 {
return token.NoPos
func (r *importReader) posv1() {
delta := r.int64()
r.prevColumn += delta >> 1
if delta&1 != 0 {
delta = r.int64()
r.prevLine += delta >> 1
if delta&1 != 0 {
r.prevFile = r.string()
}
}
return r.p.fake.pos(r.prevFile, int(r.prevLine))
}
func (r *importReader) typ() types.Type {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment