blob: 4b765777ff71420b006a94e73c36e91428544f45 [file] [log] [blame]
// go-linemap.cc -- LLVM implementation of Linemap.
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "go-location.h"
#include "go-llvm-linemap.h"
#include "llvm/Support/LEB128.h"
#include <sstream>
#include <iomanip>
static constexpr unsigned NoHandle = 0xffffffff;
Linemap* Linemap::instance_ = NULL;
Llvm_linemap::Llvm_linemap()
: Linemap()
, unknown_fidx_(0)
, builtin_fidx_(1)
, current_fidx_(0)
, current_line_(0xffffffff)
, builtin_handle_(NoHandle)
, unknown_handle_(NoHandle)
, firsthandle_(NoHandle)
, lasthandle_(NoHandle)
, lookups_(0)
, in_file_(false)
{
files_.push_back("");
files_.push_back("<built-in>");
unknown_handle_ = add_encoded_location(FLC(unknown_fidx_, 0, 0));
builtin_handle_ = add_encoded_location(FLC(builtin_fidx_, 1, 1));
segments_.push_back(Segment(unknown_handle_, unknown_handle_, unknown_fidx_));
segments_.push_back(Segment(builtin_handle_, builtin_handle_, builtin_fidx_));
}
Llvm_linemap::~Llvm_linemap()
{
}
unsigned Llvm_linemap::add_encoded_location(const FLC &flc)
{
unsigned char buf[64];
unsigned l1 = llvm::encodeULEB128(flc.line, buf);
unsigned l2 = llvm::encodeULEB128(flc.column, &buf[l1]);
unsigned tot = l1 + l2;
unsigned handle = encoded_locations_.size();
for (unsigned i = 0; i < tot; ++i)
encoded_locations_.push_back(buf[i]);
return handle;
}
Llvm_linemap::FLC Llvm_linemap::decode_location(unsigned handle)
{
assert(handle < encoded_locations_.size());
// Read line/col from encoded array
FLC rval(0, 0, 0);
unsigned char *lptr = &encoded_locations_[handle];
unsigned c1;
rval.line = llvm::decodeULEB128(lptr, &c1);
unsigned c2;
rval.column = llvm::decodeULEB128(&lptr[c1], &c2);
assert(handle + c1 + c2 <= encoded_locations_.size());
// Determine file ID by looking up handle in segment table
Segment s(handle, handle, 0);
auto it = std::lower_bound(segments_.begin(), segments_.end(), s,
Segment::cmp);
rval.fidx = (it == segments_.end() ? current_fidx_ : it->fidx);
return rval;
}
// Start getting locations from a new file.
void
Llvm_linemap::start_file(const char *file_name, unsigned line_begin)
{
if (firsthandle_ != NoHandle) {
// Close out the current segment
segments_.push_back(Segment(firsthandle_, lasthandle_, current_fidx_));
}
firsthandle_ = NoHandle;
lasthandle_ = NoHandle;
// Locate the file in the file table, adding new entry if needed
auto it = fmap_.find(std::string(file_name));
unsigned fidx = files_.size();
if (it != fmap_.end())
fidx = it->second;
else {
files_.push_back(std::string(file_name));
fmap_[file_name] = fidx;
}
current_fidx_ = fidx;
current_line_ = line_begin;
in_file_ = true;
}
// Stringify a location
std::string
Llvm_linemap::to_string(Location location)
{
if (location.handle() == unknown_handle_)
return "";
if (location.handle() == builtin_handle_)
return "<built-in>";
FLC flc = decode_location(location.handle());
const std::string &path = files_[flc.fidx];
std::stringstream ss;
ss << lbasename(path.c_str()) << ":" << flc.line;
return ss.str();
}
int
Llvm_linemap::location_line(Location loc)
{
FLC flc = decode_location(loc.handle());
return flc.line;
}
std::string
Llvm_linemap::location_file(Location loc)
{
FLC flc = decode_location(loc.handle());
return files_[flc.fidx];
}
unsigned
Llvm_linemap::location_column(Location loc)
{
FLC flc = decode_location(loc.handle());
return flc.column;
}
// Stop getting locations.
void
Llvm_linemap::stop()
{
in_file_ = false;
}
// Start a new line.
void
Llvm_linemap::start_line(unsigned lineno, unsigned linesize)
{
current_line_ = lineno;
}
// Get a location.
Location
Llvm_linemap::get_location(unsigned column)
{
assert(in_file_);
lookups_++;
FLC flc(current_fidx_, current_line_, column);
unsigned handle = add_encoded_location(flc);
if (firsthandle_ == NoHandle)
firsthandle_ = handle;
lasthandle_ = handle;
return Location(handle);
}
std::string
Llvm_linemap::get_initial_file()
{
if (files_.size() < 3)
return "";
return files_[2];
}
// Get the unknown location.
Location
Llvm_linemap::get_unknown_location()
{
return Location(unknown_handle_);
}
// Get the predeclared location.
Location
Llvm_linemap::get_predeclared_location()
{
return Location(builtin_handle_);
}
// Return whether a location is the predeclared location.
bool
Llvm_linemap::is_predeclared(Location loc)
{
return loc.handle() == builtin_handle_;
}
// Return whether a location is the unknown location.
bool
Llvm_linemap::is_unknown(Location loc)
{
return loc.handle() == unknown_handle_;
}
std::string Llvm_linemap::statistics()
{
double nbl = (encoded_locations_.size() ?
((double)encoded_locations_.size())/((double)lookups_) : 0);
std::stringstream ss;
ss << "accesses=" << lookups_
<< " files=" << files_.size()
<< " segments=" << segments_.size()
<< " locmem=" << encoded_locations_.size()
<< " bytes/location=" << std::setprecision(2) << nbl;
return ss.str();
}
void Llvm_linemap::dumpHandle(unsigned handle)
{
Location loc(handle);
std::cerr << to_string(loc);
}
void Llvm_linemap::dumpLocation(Location loc)
{
std::cerr << to_string(loc);
}
void Llvm_linemap::dump()
{
std::cerr << "Files:\n";
for (unsigned ii = 0; ii < files_.size(); ++ii)
std::cerr << ii << ": " << files_[ii] << "\n";
std::cerr << "Segments:\n";
for (unsigned ii = 0; ii < segments_.size(); ++ii) {
unsigned lo = segments_[ii].lo;
unsigned hi = segments_[ii].hi;
std::cerr << ii << ": [" << lo << "," << hi << "] lo='"
<< to_string(Location(lo)) << "' hi='"
<< to_string(Location(hi)) << "'\n";
}
std::cerr << "firsthandle: " << firsthandle_ << "\n";
std::cerr << "lasthandle: " << lasthandle_ << "\n";
}
// Return the singleton Linemap to use for the backend.
Llvm_linemap*
Llvm_linemap::instance()
{
return static_cast<Llvm_linemap*>(instance_);
}
Llvm_linemap*
go_get_llvm_linemap()
{
if (! Llvm_linemap::instance()) {
return new Llvm_linemap;
}
return Llvm_linemap::instance();
}
Linemap*
go_get_linemap()
{
return go_get_llvm_linemap();
}