8264207: CodeStrings does not honour fixed address assumption.

Reviewed-by: redestad, neliasso
This commit is contained in:
Patric Hedlin 2021-09-06 14:43:54 +00:00
parent 2cabec8ddc
commit 7bd4f496b4
16 changed files with 788 additions and 380 deletions

View file

@ -88,7 +88,7 @@ typedef CodeBuffer::csize_t csize_t; // file-local definition
// External buffer, in a predefined CodeBlob. // External buffer, in a predefined CodeBlob.
// Important: The code_start must be taken exactly, and not realigned. // Important: The code_start must be taken exactly, and not realigned.
CodeBuffer::CodeBuffer(CodeBlob* blob) { CodeBuffer::CodeBuffer(CodeBlob* blob) DEBUG_ONLY(: Scrubber(this, sizeof(*this))) {
// Provide code buffer with meaningful name // Provide code buffer with meaningful name
initialize_misc(blob->name()); initialize_misc(blob->name());
initialize(blob->content_begin(), blob->content_size()); initialize(blob->content_begin(), blob->content_size());
@ -126,11 +126,9 @@ void CodeBuffer::initialize(csize_t code_size, csize_t locs_size) {
CodeBuffer::~CodeBuffer() { CodeBuffer::~CodeBuffer() {
verify_section_allocation(); verify_section_allocation();
// If we allocate our code buffer from the CodeCache // If we allocated our code buffer from the CodeCache via a BufferBlob, and
// via a BufferBlob, and it's not permanent, then // it's not permanent, then free the BufferBlob. The rest of the memory
// free the BufferBlob. // will be freed when the ResourceObj is released.
// The rest of the memory will be freed when the ResourceObj
// is released.
for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) { for (CodeBuffer* cb = this; cb != NULL; cb = cb->before_expand()) {
// Previous incarnations of this buffer are held live, so that internal // Previous incarnations of this buffer are held live, so that internal
// addresses constructed before expansions will not be confused. // addresses constructed before expansions will not be confused.
@ -140,18 +138,9 @@ CodeBuffer::~CodeBuffer() {
// free any overflow storage // free any overflow storage
delete _overflow_arena; delete _overflow_arena;
// Claim is that stack allocation ensures resources are cleaned up. NOT_PRODUCT(clear_strings());
// This is resource clean up, let's hope that all were properly copied out.
NOT_PRODUCT(free_strings();)
#ifdef ASSERT
// Save allocation type to execute assert in ~ResourceObj()
// which is called after this destructor.
assert(_default_oop_recorder.allocated_on_stack(), "should be embedded object"); assert(_default_oop_recorder.allocated_on_stack(), "should be embedded object");
ResourceObj::allocation_type at = _default_oop_recorder.get_allocation_type();
Copy::fill_to_bytes(this, sizeof(*this), badResourceValue);
ResourceObj::set_allocation_type((address)(&_default_oop_recorder), at);
#endif
} }
void CodeBuffer::initialize_oop_recorder(OopRecorder* r) { void CodeBuffer::initialize_oop_recorder(OopRecorder* r) {
@ -715,8 +704,9 @@ void CodeBuffer::copy_code_to(CodeBlob* dest_blob) {
relocate_code_to(&dest); relocate_code_to(&dest);
// transfer strings and comments from buffer to blob // Share assembly remarks and debug strings with the blob.
NOT_PRODUCT(dest_blob->set_strings(_code_strings);) NOT_PRODUCT(dest_blob->use_remarks(_asm_remarks));
NOT_PRODUCT(dest_blob->use_strings(_dbg_strings));
// Done moving code bytes; were they the right size? // Done moving code bytes; were they the right size?
assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity"); assert((int)align_up(dest.total_content_size(), oopSize) == dest_blob->content_size(), "sanity");
@ -990,194 +980,22 @@ void CodeBuffer::log_section_sizes(const char* name) {
} }
#ifndef PRODUCT #ifndef PRODUCT
void CodeBuffer::block_comment(ptrdiff_t offset, const char* comment) {
void CodeBuffer::block_comment(intptr_t offset, const char * comment) {
if (_collect_comments) { if (_collect_comments) {
_code_strings.add_comment(offset, comment); const char* str = _asm_remarks.insert(offset, comment);
postcond(str != comment);
} }
} }
const char* CodeBuffer::code_string(const char* str) { const char* CodeBuffer::code_string(const char* str) {
return _code_strings.add_string(str); const char* tmp = _dbg_strings.insert(str);
} postcond(tmp != str);
return tmp;
class CodeString: public CHeapObj<mtCode> {
private:
friend class CodeStrings;
const char * _string;
CodeString* _next;
CodeString* _prev;
intptr_t _offset;
static long allocated_code_strings;
~CodeString() {
assert(_next == NULL && _prev == NULL, "wrong interface for freeing list");
allocated_code_strings--;
log_trace(codestrings)("Freeing CodeString [%s] (%p)", _string, (void*)_string);
os::free((void*)_string);
}
bool is_comment() const { return _offset >= 0; }
public:
CodeString(const char * string, intptr_t offset = -1)
: _next(NULL), _prev(NULL), _offset(offset) {
allocated_code_strings++;
_string = os::strdup(string, mtCode);
log_trace(codestrings)("Created CodeString [%s] (%p)", _string, (void*)_string);
}
const char * string() const { return _string; }
intptr_t offset() const { assert(_offset >= 0, "offset for non comment?"); return _offset; }
CodeString* next() const { return _next; }
void set_next(CodeString* next) {
_next = next;
if (next != NULL) {
next->_prev = this;
}
}
CodeString* first_comment() {
if (is_comment()) {
return this;
} else {
return next_comment();
}
}
CodeString* next_comment() const {
CodeString* s = _next;
while (s != NULL && !s->is_comment()) {
s = s->_next;
}
return s;
}
};
// For tracing statistics. Will use raw increment/decrement, so it might not be
// exact
long CodeString::allocated_code_strings = 0;
CodeString* CodeStrings::find(intptr_t offset) const {
CodeString* a = _strings->first_comment();
while (a != NULL && a->offset() != offset) {
a = a->next_comment();
}
return a;
}
// Convenience for add_comment.
CodeString* CodeStrings::find_last(intptr_t offset) const {
CodeString* a = _strings_last;
while (a != NULL && !(a->is_comment() && a->offset() == offset)) {
a = a->_prev;
}
return a;
}
void CodeStrings::add_comment(intptr_t offset, const char * comment) {
check_valid();
CodeString* c = new CodeString(comment, offset);
CodeString* inspos = (_strings == NULL) ? NULL : find_last(offset);
if (inspos != NULL) {
// insert after already existing comments with same offset
c->set_next(inspos->next());
inspos->set_next(c);
} else {
// no comments with such offset, yet. Insert before anything else.
c->set_next(_strings);
_strings = c;
}
if (c->next() == NULL) {
_strings_last = c;
}
}
// Deep copy of CodeStrings for consistent memory management.
void CodeStrings::copy(CodeStrings& other) {
log_debug(codestrings)("Copying %d Codestring(s)", other.count());
other.check_valid();
check_valid();
assert(is_null(), "Cannot copy onto non-empty CodeStrings");
CodeString* n = other._strings;
CodeString** ps = &_strings;
CodeString* prev = NULL;
while (n != NULL) {
if (n->is_comment()) {
*ps = new CodeString(n->string(), n->offset());
} else {
*ps = new CodeString(n->string());
}
(*ps)->_prev = prev;
prev = *ps;
ps = &((*ps)->_next);
n = n->next();
}
}
const char* CodeStrings::_prefix = " ;; "; // default: can be changed via set_prefix
void CodeStrings::print_block_comment(outputStream* stream, intptr_t offset) const {
check_valid();
if (_strings != NULL) {
CodeString* c = find(offset);
while (c && c->offset() == offset) {
stream->bol();
stream->print("%s", _prefix);
// Don't interpret as format strings since it could contain %
stream->print_raw(c->string());
stream->bol(); // advance to next line only if string didn't contain a cr() at the end.
c = c->next_comment();
}
}
}
int CodeStrings::count() const {
int i = 0;
CodeString* s = _strings;
while (s != NULL) {
i++;
s = s->_next;
}
return i;
}
// Also sets is_null()
void CodeStrings::free() {
log_debug(codestrings)("Freeing %d out of approx. %ld CodeString(s), ", count(), CodeString::allocated_code_strings);
CodeString* n = _strings;
while (n) {
// unlink the node from the list saving a pointer to the next
CodeString* p = n->next();
n->set_next(NULL);
if (p != NULL) {
assert(p->_prev == n, "missing prev link");
p->_prev = NULL;
}
delete n;
n = p;
}
set_null_and_invalidate();
}
const char* CodeStrings::add_string(const char * string) {
check_valid();
CodeString* s = new CodeString(string);
s->set_next(_strings);
if (_strings == NULL) {
_strings_last = s;
}
_strings = s;
assert(s->string() != NULL, "should have a string");
return s->string();
} }
void CodeBuffer::decode() { void CodeBuffer::decode() {
ttyLocker ttyl; ttyLocker ttyl;
Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &strings())); Disassembler::decode(decode_begin(), insts_end(), tty NOT_PRODUCT(COMMA &asm_remarks()));
_decode_begin = insts_end(); _decode_begin = insts_end();
} }
@ -1207,4 +1025,297 @@ void CodeBuffer::print() {
} }
} }
#endif // PRODUCT // ----- CHeapString -----------------------------------------------------------
class CHeapString : public CHeapObj<mtCode> {
public:
CHeapString(const char* str) : _string(os::strdup(str)) {}
~CHeapString() {
os::free((void*)_string);
_string = nullptr;
}
const char* string() const { return _string; }
private:
const char* _string;
};
// ----- AsmRemarkCollection ---------------------------------------------------
class AsmRemarkCollection : public CHeapObj<mtCode> {
public:
AsmRemarkCollection() : _ref_cnt(1), _remarks(nullptr), _next(nullptr) {}
~AsmRemarkCollection() {
assert(is_empty(), "Must 'clear()' before deleting!");
assert(_ref_cnt == 0, "No uses must remain when deleting!");
}
AsmRemarkCollection* reuse() {
precond(_ref_cnt > 0);
return _ref_cnt++, this;
}
const char* insert(uint offset, const char* remark);
const char* lookup(uint offset) const;
const char* next(uint offset) const;
bool is_empty() const { return _remarks == nullptr; }
uint clear();
private:
struct Cell : CHeapString {
Cell(const char* remark, uint offset) :
CHeapString(remark), offset(offset), prev(nullptr), next(nullptr) {}
void push_back(Cell* cell) {
Cell* head = this;
Cell* tail = prev;
tail->next = cell;
cell->next = head;
cell->prev = tail;
prev = cell;
}
uint offset;
Cell* prev;
Cell* next;
};
uint _ref_cnt;
Cell* _remarks;
// Using a 'mutable' iteration pointer to allow 'const' on lookup/next (that
// does not change the state of the list per se), supportig a simplistic
// iteration scheme.
mutable Cell* _next;
};
// ----- DbgStringCollection ---------------------------------------------------
class DbgStringCollection : public CHeapObj<mtCode> {
public:
DbgStringCollection() : _ref_cnt(1), _strings(nullptr) {}
~DbgStringCollection() {
assert(is_empty(), "Must 'clear()' before deleting!");
assert(_ref_cnt == 0, "No uses must remain when deleting!");
}
DbgStringCollection* reuse() {
precond(_ref_cnt > 0);
return _ref_cnt++, this;
}
const char* insert(const char* str);
const char* lookup(const char* str) const;
bool is_empty() const { return _strings == nullptr; }
uint clear();
private:
struct Cell : CHeapString {
Cell(const char* dbgstr) :
CHeapString(dbgstr), prev(nullptr), next(nullptr) {}
void push_back(Cell* cell) {
Cell* head = this;
Cell* tail = prev;
tail->next = cell;
cell->next = head;
cell->prev = tail;
prev = cell;
}
Cell* prev;
Cell* next;
};
uint _ref_cnt;
Cell* _strings;
};
// ----- AsmRemarks ------------------------------------------------------------
//
// Acting as interface to reference counted mapping [offset -> remark], where
// offset is a byte offset into an instruction stream (CodeBuffer, CodeBlob or
// other memory buffer) and remark is a string (comment).
//
AsmRemarks::AsmRemarks() : _remarks(new AsmRemarkCollection()) {
assert(_remarks != nullptr, "Allocation failure!");
}
AsmRemarks::~AsmRemarks() {
assert(_remarks == nullptr, "Must 'clear()' before deleting!");
}
const char* AsmRemarks::insert(uint offset, const char* remstr) {
precond(remstr != nullptr);
return _remarks->insert(offset, remstr);
}
bool AsmRemarks::is_empty() const {
return _remarks->is_empty();
}
void AsmRemarks::share(const AsmRemarks &src) {
precond(is_empty());
clear();
_remarks = src._remarks->reuse();
}
void AsmRemarks::clear() {
if (_remarks->clear() == 0) {
delete _remarks;
}
_remarks = nullptr;
}
uint AsmRemarks::print(uint offset, outputStream* strm) const {
uint count = 0;
const char* prefix = " ;; ";
const char* remstr = _remarks->lookup(offset);
while (remstr != nullptr) {
strm->bol();
strm->print("%s", prefix);
// Don't interpret as format strings since it could contain '%'.
strm->print_raw(remstr);
// Advance to next line iff string didn't contain a cr() at the end.
strm->bol();
remstr = _remarks->next(offset);
count++;
}
return count;
}
// ----- DbgStrings ------------------------------------------------------------
//
// Acting as interface to reference counted collection of (debug) strings used
// in the code generated, and thus requiring a fixed address.
//
DbgStrings::DbgStrings() : _strings(new DbgStringCollection()) {
assert(_strings != nullptr, "Allocation failure!");
}
DbgStrings::~DbgStrings() {
assert(_strings == nullptr, "Must 'clear()' before deleting!");
}
const char* DbgStrings::insert(const char* dbgstr) {
const char* str = _strings->lookup(dbgstr);
return str != nullptr ? str : _strings->insert(dbgstr);
}
bool DbgStrings::is_empty() const {
return _strings->is_empty();
}
void DbgStrings::share(const DbgStrings &src) {
precond(is_empty());
_strings = src._strings->reuse();
}
void DbgStrings::clear() {
if (_strings->clear() == 0) {
delete _strings;
}
_strings = nullptr;
}
// ----- AsmRemarkCollection ---------------------------------------------------
const char* AsmRemarkCollection::insert(uint offset, const char* remstr) {
precond(remstr != nullptr);
Cell* cell = new Cell { remstr, offset };
if (is_empty()) {
cell->prev = cell;
cell->next = cell;
_remarks = cell;
} else {
_remarks->push_back(cell);
}
return cell->string();
}
const char* AsmRemarkCollection::lookup(uint offset) const {
_next = _remarks;
return next(offset);
}
const char* AsmRemarkCollection::next(uint offset) const {
if (_next != nullptr) {
Cell* i = _next;
do {
if (i->offset == offset) {
_next = i->next == _remarks ? nullptr : i->next;
return i->string();
}
i = i->next;
} while (i != _remarks);
_next = nullptr;
}
return nullptr;
}
uint AsmRemarkCollection::clear() {
precond(_ref_cnt > 0);
if (--_ref_cnt > 0) {
return _ref_cnt;
}
if (!is_empty()) {
uint count = 0;
Cell* i = _remarks;
do {
Cell* next = i->next;
delete i;
i = next;
count++;
} while (i != _remarks);
log_debug(codestrings)("Clear %u asm-remark%s.", count, count == 1 ? "" : "s");
_remarks = nullptr;
}
return 0; // i.e. _ref_cnt == 0
}
// ----- DbgStringCollection ---------------------------------------------------
const char* DbgStringCollection::insert(const char* dbgstr) {
precond(dbgstr != nullptr);
Cell* cell = new Cell { dbgstr };
if (is_empty()) {
cell->prev = cell;
cell->next = cell;
_strings = cell;
} else {
_strings->push_back(cell);
}
return cell->string();
}
const char* DbgStringCollection::lookup(const char* dbgstr) const {
precond(dbgstr != nullptr);
if (_strings != nullptr) {
Cell* i = _strings;
do {
if (strcmp(i->string(), dbgstr) == 0) {
return i->string();
}
i = i->next;
} while (i != _strings);
}
return nullptr;
}
uint DbgStringCollection::clear() {
precond(_ref_cnt > 0);
if (--_ref_cnt > 0) {
return _ref_cnt;
}
if (!is_empty()) {
uint count = 0;
Cell* i = _strings;
do {
Cell* next = i->next;
delete i;
i = next;
count++;
} while (i != _strings);
log_debug(codestrings)("Clear %u dbg-string%s.", count, count == 1 ? "" : "s");
_strings = nullptr;
}
return 0; // i.e. _ref_cnt == 0
}
#endif // not PRODUCT

View file

@ -31,7 +31,6 @@
#include "utilities/debug.hpp" #include "utilities/debug.hpp"
#include "utilities/macros.hpp" #include "utilities/macros.hpp"
class CodeStrings;
class PhaseCFG; class PhaseCFG;
class Compile; class Compile;
class BufferBlob; class BufferBlob;
@ -273,71 +272,76 @@ class CodeSection {
#endif //PRODUCT #endif //PRODUCT
}; };
class CodeString;
class CodeStrings { #ifndef PRODUCT
class AsmRemarkCollection;
class DbgStringCollection;
// The assumption made here is that most code remarks (or comments) added to
// the generated assembly code are unique, i.e. there is very little gain in
// trying to share the strings between the different offsets tracked in a
// buffer (or blob).
class AsmRemarks {
public:
AsmRemarks();
~AsmRemarks();
const char* insert(uint offset, const char* remstr);
bool is_empty() const;
void share(const AsmRemarks &src);
void clear();
uint print(uint offset, outputStream* strm = tty) const;
// For testing purposes only.
const AsmRemarkCollection* ref() const { return _remarks; }
private: private:
#ifndef PRODUCT AsmRemarkCollection* _remarks;
CodeString* _strings;
CodeString* _strings_last;
#ifdef ASSERT
// Becomes true after copy-out, forbids further use.
bool _defunct; // Zero bit pattern is "valid", see memset call in decode_env::decode_env
#endif
static const char* _prefix; // defaults to " ;; "
CodeString* find(intptr_t offset) const;
CodeString* find_last(intptr_t offset) const;
void set_null_and_invalidate() {
_strings = NULL;
_strings_last = NULL;
#ifdef ASSERT
_defunct = true;
#endif
}
#endif
public:
CodeStrings() {
#ifndef PRODUCT
_strings = NULL;
_strings_last = NULL;
#ifdef ASSERT
_defunct = false;
#endif
#endif
}
#ifndef PRODUCT
bool is_null() {
#ifdef ASSERT
return _strings == NULL;
#else
return true;
#endif
}
const char* add_string(const char * string);
void add_comment(intptr_t offset, const char * comment);
void print_block_comment(outputStream* stream, intptr_t offset) const;
int count() const;
// COPY strings from other to this; leave other valid.
void copy(CodeStrings& other);
// FREE strings; invalidate this.
void free();
// Guarantee that _strings are used at most once; assign and free invalidate a buffer.
inline void check_valid() const {
assert(!_defunct, "Use of invalid CodeStrings");
}
static void set_prefix(const char *prefix) {
_prefix = prefix;
}
#endif // !PRODUCT
}; };
// The assumption made here is that the number of debug strings (with a fixed
// address requirement) is a rather small set per compilation unit.
class DbgStrings {
public:
DbgStrings();
~DbgStrings();
const char* insert(const char* dbgstr);
bool is_empty() const;
void share(const DbgStrings &src);
void clear();
// For testing purposes only.
const DbgStringCollection* ref() const { return _strings; }
private:
DbgStringCollection* _strings;
};
#endif // not PRODUCT
#ifdef ASSERT
#include "utilities/copy.hpp"
class Scrubber {
public:
Scrubber(void* addr, size_t size) : _addr(addr), _size(size) {}
~Scrubber() {
Copy::fill_to_bytes(_addr, _size, badResourceValue);
}
private:
void* _addr;
size_t _size;
};
#endif // ASSERT
// A CodeBuffer describes a memory space into which assembly // A CodeBuffer describes a memory space into which assembly
// code is generated. This memory space usually occupies the // code is generated. This memory space usually occupies the
// interior of a single BufferBlob, but in some cases it may be // interior of a single BufferBlob, but in some cases it may be
@ -362,7 +366,7 @@ public:
// Instructions and data in one section can contain relocatable references to // Instructions and data in one section can contain relocatable references to
// addresses in a sibling section. // addresses in a sibling section.
class CodeBuffer: public StackObj { class CodeBuffer: public StackObj DEBUG_ONLY(COMMA private Scrubber) {
friend class CodeSection; friend class CodeSection;
friend class StubCodeGenerator; friend class StubCodeGenerator;
@ -411,7 +415,8 @@ class CodeBuffer: public StackObj {
address _last_insn; // used to merge consecutive memory barriers, loads or stores. address _last_insn; // used to merge consecutive memory barriers, loads or stores.
#ifndef PRODUCT #ifndef PRODUCT
CodeStrings _code_strings; AsmRemarks _asm_remarks;
DbgStrings _dbg_strings;
bool _collect_comments; // Indicate if we need to collect block comments at all. bool _collect_comments; // Indicate if we need to collect block comments at all.
address _decode_begin; // start address for decode address _decode_begin; // start address for decode
address decode_begin(); address decode_begin();
@ -429,7 +434,6 @@ class CodeBuffer: public StackObj {
#ifndef PRODUCT #ifndef PRODUCT
_decode_begin = NULL; _decode_begin = NULL;
_code_strings = CodeStrings();
// Collect block comments, but restrict collection to cases where a disassembly is output. // Collect block comments, but restrict collection to cases where a disassembly is output.
_collect_comments = ( PrintAssembly _collect_comments = ( PrintAssembly
|| PrintStubCode || PrintStubCode
@ -484,7 +488,9 @@ class CodeBuffer: public StackObj {
public: public:
// (1) code buffer referring to pre-allocated instruction memory // (1) code buffer referring to pre-allocated instruction memory
CodeBuffer(address code_start, csize_t code_size) { CodeBuffer(address code_start, csize_t code_size)
DEBUG_ONLY(: Scrubber(this, sizeof(*this)))
{
assert(code_start != NULL, "sanity"); assert(code_start != NULL, "sanity");
initialize_misc("static buffer"); initialize_misc("static buffer");
initialize(code_start, code_size); initialize(code_start, code_size);
@ -497,14 +503,18 @@ class CodeBuffer: public StackObj {
// (3) code buffer allocating codeBlob memory for code & relocation // (3) code buffer allocating codeBlob memory for code & relocation
// info but with lazy initialization. The name must be something // info but with lazy initialization. The name must be something
// informative. // informative.
CodeBuffer(const char* name) { CodeBuffer(const char* name)
DEBUG_ONLY(: Scrubber(this, sizeof(*this)))
{
initialize_misc(name); initialize_misc(name);
} }
// (4) code buffer allocating codeBlob memory for code & relocation // (4) code buffer allocating codeBlob memory for code & relocation
// info. The name must be something informative and code_size must // info. The name must be something informative and code_size must
// include both code and stubs sizes. // include both code and stubs sizes.
CodeBuffer(const char* name, csize_t code_size, csize_t locs_size) { CodeBuffer(const char* name, csize_t code_size, csize_t locs_size)
DEBUG_ONLY(: Scrubber(this, sizeof(*this)))
{
initialize_misc(name); initialize_misc(name);
initialize(code_size, locs_size); initialize(code_size, locs_size);
} }
@ -634,12 +644,12 @@ class CodeBuffer: public StackObj {
void clear_last_insn() { set_last_insn(NULL); } void clear_last_insn() { set_last_insn(NULL); }
#ifndef PRODUCT #ifndef PRODUCT
CodeStrings& strings() { return _code_strings; } AsmRemarks &asm_remarks() { return _asm_remarks; }
DbgStrings &dbg_strings() { return _dbg_strings; }
void free_strings() { void clear_strings() {
if (!_code_strings.is_null()) { _asm_remarks.clear();
_code_strings.free(); // sets _strings Null as a side-effect. _dbg_strings.clear();
}
} }
#endif #endif
@ -666,7 +676,7 @@ class CodeBuffer: public StackObj {
} }
} }
void block_comment(intptr_t offset, const char * comment) PRODUCT_RETURN; void block_comment(ptrdiff_t offset, const char* comment) PRODUCT_RETURN;
const char* code_string(const char* str) PRODUCT_RETURN_(return NULL;); const char* code_string(const char* str) PRODUCT_RETURN_(return NULL;);
// Log a little info about section usage in the CodeBuffer // Log a little info about section usage in the CodeBuffer

View file

@ -94,7 +94,6 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la
_oop_maps(oop_maps), _oop_maps(oop_maps),
_caller_must_gc_arguments(caller_must_gc_arguments), _caller_must_gc_arguments(caller_must_gc_arguments),
_name(name) _name(name)
NOT_PRODUCT(COMMA _strings(CodeStrings()))
{ {
assert(is_aligned(layout.size(), oopSize), "unaligned size"); assert(is_aligned(layout.size(), oopSize), "unaligned size");
assert(is_aligned(layout.header_size(), oopSize), "unaligned size"); assert(is_aligned(layout.header_size(), oopSize), "unaligned size");
@ -107,7 +106,7 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la
S390_ONLY(_ctable_offset = 0;) // avoid uninitialized fields S390_ONLY(_ctable_offset = 0;) // avoid uninitialized fields
} }
CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) : CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb /*UNUSED*/, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) :
_type(type), _type(type),
_size(layout.size()), _size(layout.size()),
_header_size(layout.header_size()), _header_size(layout.header_size()),
@ -122,7 +121,6 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la
_relocation_end(layout.relocation_end()), _relocation_end(layout.relocation_end()),
_caller_must_gc_arguments(caller_must_gc_arguments), _caller_must_gc_arguments(caller_must_gc_arguments),
_name(name) _name(name)
NOT_PRODUCT(COMMA _strings(CodeStrings()))
{ {
assert(is_aligned(_size, oopSize), "unaligned size"); assert(is_aligned(_size, oopSize), "unaligned size");
assert(is_aligned(_header_size, oopSize), "unaligned size"); assert(is_aligned(_header_size, oopSize), "unaligned size");
@ -164,7 +162,8 @@ RuntimeBlob::RuntimeBlob(
void CodeBlob::flush() { void CodeBlob::flush() {
FREE_C_HEAP_ARRAY(unsigned char, _oop_maps); FREE_C_HEAP_ARRAY(unsigned char, _oop_maps);
_oop_maps = NULL; _oop_maps = NULL;
NOT_PRODUCT(_strings.free();) NOT_PRODUCT(_asm_remarks.clear());
NOT_PRODUCT(_dbg_strings.clear());
} }
void CodeBlob::set_oop_maps(OopMapSet* p) { void CodeBlob::set_oop_maps(OopMapSet* p) {
@ -190,7 +189,8 @@ void RuntimeBlob::trace_new_stub(RuntimeBlob* stub, const char* name1, const cha
ttyLocker ttyl; ttyLocker ttyl;
tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub); tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub);
Disassembler::decode(stub->code_begin(), stub->code_end(), tty); Disassembler::decode(stub->code_begin(), stub->code_end(), tty
NOT_PRODUCT(COMMA &stub->asm_remarks()));
if ((stub->oop_maps() != NULL) && AbstractDisassembler::show_structs()) { if ((stub->oop_maps() != NULL) && AbstractDisassembler::show_structs()) {
tty->print_cr("- - - [OOP MAPS]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("- - - [OOP MAPS]- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
stub->oop_maps()->print(); stub->oop_maps()->print();

View file

@ -112,15 +112,22 @@ protected:
const char* _name; const char* _name;
S390_ONLY(int _ctable_offset;) S390_ONLY(int _ctable_offset;)
NOT_PRODUCT(CodeStrings _strings;) #ifndef PRODUCT
AsmRemarks _asm_remarks;
DbgStrings _dbg_strings;
~CodeBlob() {
_asm_remarks.clear();
_dbg_strings.clear();
}
#endif // not PRODUCT
CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments); CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments);
CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments); CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments);
public: public:
// Only used by unit test. // Only used by unit test.
CodeBlob() CodeBlob() : _type(compiler_none) {}
: _type(compiler_none) {}
// Returns the space needed for CodeBlob // Returns the space needed for CodeBlob
static unsigned int allocation_size(CodeBuffer* cb, int header_size); static unsigned int allocation_size(CodeBuffer* cb, int header_size);
@ -232,18 +239,21 @@ public:
void dump_for_addr(address addr, outputStream* st, bool verbose) const; void dump_for_addr(address addr, outputStream* st, bool verbose) const;
void print_code(); void print_code();
// Print the comment associated with offset on stream, if there is one // Print to stream, any comments associated with offset.
virtual void print_block_comment(outputStream* stream, address block_begin) const { virtual void print_block_comment(outputStream* stream, address block_begin) const {
#ifndef PRODUCT #ifndef PRODUCT
intptr_t offset = (intptr_t)(block_begin - code_begin()); ptrdiff_t offset = block_begin - code_begin();
_strings.print_block_comment(stream, offset); assert(offset >= 0, "Expecting non-negative offset!");
#endif _asm_remarks.print(uint(offset), stream);
#endif
} }
#ifndef PRODUCT #ifndef PRODUCT
void set_strings(CodeStrings& strings) { AsmRemarks &asm_remarks() { return _asm_remarks; }
_strings.copy(strings); DbgStrings &dbg_strings() { return _dbg_strings; }
}
void use_remarks(AsmRemarks &remarks) { _asm_remarks.share(remarks); }
void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); }
#endif #endif
}; };

View file

@ -56,8 +56,7 @@ class ICStub: public Stub {
protected: protected:
friend class ICStubInterface; friend class ICStubInterface;
// This will be called only by ICStubInterface // This will be called only by ICStubInterface
void initialize(int size, void initialize(int size) { _size = size; _ic_site = NULL; }
CodeStrings strings) { _size = size; _ic_site = NULL; }
void finalize(); // called when a method is removed void finalize(); // called when a method is removed
// General info // General info

View file

@ -109,8 +109,7 @@ Stub* StubQueue::stub_containing(address pc) const {
Stub* StubQueue::request_committed(int code_size) { Stub* StubQueue::request_committed(int code_size) {
Stub* s = request(code_size); Stub* s = request(code_size);
CodeStrings strings; if (s != NULL) commit(code_size);
if (s != NULL) commit(code_size, strings);
return s; return s;
} }
@ -127,8 +126,7 @@ Stub* StubQueue::request(int requested_code_size) {
assert(_buffer_limit == _buffer_size, "buffer must be fully usable"); assert(_buffer_limit == _buffer_size, "buffer must be fully usable");
if (_queue_end + requested_size <= _buffer_size) { if (_queue_end + requested_size <= _buffer_size) {
// code fits in at the end => nothing to do // code fits in at the end => nothing to do
CodeStrings strings; stub_initialize(s, requested_size);
stub_initialize(s, requested_size, strings);
return s; return s;
} else { } else {
// stub doesn't fit in at the queue end // stub doesn't fit in at the queue end
@ -145,8 +143,7 @@ Stub* StubQueue::request(int requested_code_size) {
// Queue: |XXX|.......|XXXXXXX|.......| // Queue: |XXX|.......|XXXXXXX|.......|
// ^0 ^end ^begin ^limit ^size // ^0 ^end ^begin ^limit ^size
s = current_stub(); s = current_stub();
CodeStrings strings; stub_initialize(s, requested_size);
stub_initialize(s, requested_size, strings);
return s; return s;
} }
// Not enough space left // Not enough space left
@ -155,12 +152,12 @@ Stub* StubQueue::request(int requested_code_size) {
} }
void StubQueue::commit(int committed_code_size, CodeStrings& strings) { void StubQueue::commit(int committed_code_size) {
assert(committed_code_size > 0, "committed_code_size must be > 0"); assert(committed_code_size > 0, "committed_code_size must be > 0");
int committed_size = align_up(stub_code_size_to_size(committed_code_size), CodeEntryAlignment); int committed_size = align_up(stub_code_size_to_size(committed_code_size), CodeEntryAlignment);
Stub* s = current_stub(); Stub* s = current_stub();
assert(committed_size <= stub_size(s), "committed size must not exceed requested size"); assert(committed_size <= stub_size(s), "committed size must not exceed requested size");
stub_initialize(s, committed_size, strings); stub_initialize(s, committed_size);
_queue_end += committed_size; _queue_end += committed_size;
_number_of_stubs++; _number_of_stubs++;
if (_mutex != NULL) _mutex->unlock(); if (_mutex != NULL) _mutex->unlock();

View file

@ -60,8 +60,7 @@
class Stub { class Stub {
public: public:
// Initialization/finalization // Initialization/finalization
void initialize(int size, void initialize(int size) { ShouldNotCallThis(); } // called to initialize/specify the stub's size
CodeStrings& strings) { ShouldNotCallThis(); } // called to initialize/specify the stub's size
void finalize() { ShouldNotCallThis(); } // called before the stub is deallocated void finalize() { ShouldNotCallThis(); } // called before the stub is deallocated
// General info/converters // General info/converters
@ -94,8 +93,7 @@ class Stub {
class StubInterface: public CHeapObj<mtCode> { class StubInterface: public CHeapObj<mtCode> {
public: public:
// Initialization/finalization // Initialization/finalization
virtual void initialize(Stub* self, int size, virtual void initialize(Stub* self, int size) = 0; // called after creation (called twice if allocated via (request, commit))
CodeStrings& strings) = 0; // called after creation (called twice if allocated via (request, commit))
virtual void finalize(Stub* self) = 0; // called before deallocation virtual void finalize(Stub* self) = 0; // called before deallocation
// General info/converters // General info/converters
@ -123,8 +121,7 @@ class StubInterface: public CHeapObj<mtCode> {
\ \
public: \ public: \
/* Initialization/finalization */ \ /* Initialization/finalization */ \
virtual void initialize(Stub* self, int size, \ virtual void initialize(Stub* self, int size) { cast(self)->initialize(size); } \
CodeStrings& strings) { cast(self)->initialize(size, strings); } \
virtual void finalize(Stub* self) { cast(self)->finalize(); } \ virtual void finalize(Stub* self) { cast(self)->finalize(); } \
\ \
/* General info */ \ /* General info */ \
@ -163,8 +160,7 @@ class StubQueue: public CHeapObj<mtCode> {
Stub* current_stub() const { return stub_at(_queue_end); } Stub* current_stub() const { return stub_at(_queue_end); }
// Stub functionality accessed via interface // Stub functionality accessed via interface
void stub_initialize(Stub* s, int size, void stub_initialize(Stub* s, int size) { assert(size % CodeEntryAlignment == 0, "size not aligned"); _stub_interface->initialize(s, size); }
CodeStrings& strings) { assert(size % CodeEntryAlignment == 0, "size not aligned"); _stub_interface->initialize(s, size, strings); }
void stub_finalize(Stub* s) { _stub_interface->finalize(s); } void stub_finalize(Stub* s) { _stub_interface->finalize(s); }
int stub_size(Stub* s) const { return _stub_interface->size(s); } int stub_size(Stub* s) const { return _stub_interface->size(s); }
bool stub_contains(Stub* s, address pc) const { return _stub_interface->code_begin(s) <= pc && pc < _stub_interface->code_end(s); } bool stub_contains(Stub* s, address pc) const { return _stub_interface->code_begin(s) <= pc && pc < _stub_interface->code_end(s); }
@ -191,8 +187,7 @@ class StubQueue: public CHeapObj<mtCode> {
// Stub allocation (atomic transactions) // Stub allocation (atomic transactions)
Stub* request_committed(int code_size); // request a stub that provides exactly code_size space for code Stub* request_committed(int code_size); // request a stub that provides exactly code_size space for code
Stub* request(int requested_code_size); // request a stub with a (maximum) code space - locks the queue Stub* request(int requested_code_size); // request a stub with a (maximum) code space - locks the queue
void commit (int committed_code_size, void commit (int committed_code_size); // commit the previously requested stub - unlocks the queue
CodeStrings& strings); // commit the previously requested stub - unlocks the queue
// Stub deallocation // Stub deallocation
void remove_first(); // remove the first stub in the queue void remove_first(); // remove the first stub in the queue

View file

@ -72,7 +72,10 @@ class decode_env {
bool _print_help; bool _print_help;
bool _helpPrinted; bool _helpPrinted;
static bool _optionsParsed; static bool _optionsParsed;
NOT_PRODUCT(const CodeStrings* _strings;) #ifndef PRODUCT
const AsmRemarks* _remarks; // Used with start/end range to provide code remarks.
ptrdiff_t _disp; // Adjustment to offset -> remark mapping.
#endif
enum { enum {
tabspacing = 8 tabspacing = 8
@ -206,7 +209,8 @@ class decode_env {
decode_env(nmethod* code, outputStream* output); decode_env(nmethod* code, outputStream* output);
// Constructor for a 'decode_env' to decode an arbitrary // Constructor for a 'decode_env' to decode an arbitrary
// piece of memory, hopefully containing code. // piece of memory, hopefully containing code.
decode_env(address start, address end, outputStream* output, const CodeStrings* strings = NULL); decode_env(address start, address end, outputStream* output
NOT_PRODUCT(COMMA const AsmRemarks* remarks = NULL COMMA ptrdiff_t disp = 0));
// Add 'original_start' argument which is the the original address // Add 'original_start' argument which is the the original address
// the instructions were located at (if this is not equal to 'start'). // the instructions were located at (if this is not equal to 'start').
@ -324,11 +328,11 @@ decode_env::decode_env(CodeBlob* code, outputStream* output) :
_print_file_name(false), _print_file_name(false),
_print_help(false), _print_help(false),
_helpPrinted(false) _helpPrinted(false)
NOT_PRODUCT(COMMA _strings(NULL)) { NOT_PRODUCT(COMMA _remarks(nullptr))
NOT_PRODUCT(COMMA _disp(0))
{
memset(_option_buf, 0, sizeof(_option_buf)); memset(_option_buf, 0, sizeof(_option_buf));
process_options(_output); process_options(_output);
} }
decode_env::decode_env(nmethod* code, outputStream* output) : decode_env::decode_env(nmethod* code, outputStream* output) :
@ -346,15 +350,17 @@ decode_env::decode_env(nmethod* code, outputStream* output) :
_print_file_name(false), _print_file_name(false),
_print_help(false), _print_help(false),
_helpPrinted(false) _helpPrinted(false)
NOT_PRODUCT(COMMA _strings(NULL)) { NOT_PRODUCT(COMMA _remarks(nullptr))
NOT_PRODUCT(COMMA _disp(0))
{
memset(_option_buf, 0, sizeof(_option_buf)); memset(_option_buf, 0, sizeof(_option_buf));
process_options(_output); process_options(_output);
} }
// Constructor for a 'decode_env' to decode a memory range [start, end) // Constructor for a 'decode_env' to decode a memory range [start, end)
// of unknown origin, assuming it contains code. // of unknown origin, assuming it contains code.
decode_env::decode_env(address start, address end, outputStream* output, const CodeStrings* c) : decode_env::decode_env(address start, address end, outputStream* output
NOT_PRODUCT(COMMA const AsmRemarks* remarks COMMA ptrdiff_t disp)) :
_output(output ? output : tty), _output(output ? output : tty),
_codeBlob(NULL), _codeBlob(NULL),
_nm(NULL), _nm(NULL),
@ -369,8 +375,9 @@ decode_env::decode_env(address start, address end, outputStream* output, const C
_print_file_name(false), _print_file_name(false),
_print_help(false), _print_help(false),
_helpPrinted(false) _helpPrinted(false)
NOT_PRODUCT(COMMA _strings(c)) { NOT_PRODUCT(COMMA _remarks(remarks))
NOT_PRODUCT(COMMA _disp(disp))
{
assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end));
memset(_option_buf, 0, sizeof(_option_buf)); memset(_option_buf, 0, sizeof(_option_buf));
process_options(_output); process_options(_output);
@ -637,15 +644,15 @@ void decode_env::print_insn_labels() {
//---< Block comments for nmethod >--- //---< Block comments for nmethod >---
// Outputs a bol() before and a cr() after, but only if a comment is printed. // Outputs a bol() before and a cr() after, but only if a comment is printed.
// Prints nmethod_section_label as well. // Prints nmethod_section_label as well.
if (_nm != NULL) { if (_nm != nullptr) {
_nm->print_block_comment(st, p); _nm->print_block_comment(st, p);
} }
if (_codeBlob != NULL) { else if (_codeBlob != nullptr) {
_codeBlob->print_block_comment(st, p); _codeBlob->print_block_comment(st, p);
} }
#ifndef PRODUCT #ifndef PRODUCT
if (_strings != NULL) { else if (_remarks != nullptr) {
_strings->print_block_comment(st, (intptr_t)(p - _start)); _remarks->print((p - _start) + _disp, st);
} }
#endif #endif
} }
@ -908,7 +915,8 @@ void Disassembler::decode(nmethod* nm, outputStream* st) {
} }
// Decode a range, given as [start address, end address) // Decode a range, given as [start address, end address)
void Disassembler::decode(address start, address end, outputStream* st, const CodeStrings* c) { void Disassembler::decode(address start, address end, outputStream* st
NOT_PRODUCT(COMMA const AsmRemarks* remarks COMMA ptrdiff_t disp)) {
#if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY)
//---< Test memory before decoding >--- //---< Test memory before decoding >---
if (!os::is_readable_range(start, end)) { if (!os::is_readable_range(start, end)) {
@ -921,22 +929,9 @@ void Disassembler::decode(address start, address end, outputStream* st, const Co
if (is_abstract()) { if (is_abstract()) {
AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen()); AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen());
return; } else {
}
// Don't do that fancy stuff. If we just have two addresses, live with it
// and treat the memory contents as "amorphic" piece of code.
#if 0
CodeBlob* cb = CodeCache::find_blob_unsafe(start);
if (cb != NULL) {
// If we have an CodeBlob at hand,
// call the specialized decoder directly.
decode(cb, st, c);
} else
#endif
{
// This seems to be just a chunk of memory. // This seems to be just a chunk of memory.
decode_env env(start, end, st, c); decode_env env(start, end, st NOT_PRODUCT(COMMA remarks COMMA disp));
env.output()->print_cr("--------------------------------------------------------------------------------"); env.output()->print_cr("--------------------------------------------------------------------------------");
env.decode_instructions(start, end); env.decode_instructions(start, end);
env.output()->print_cr("--------------------------------------------------------------------------------"); env.output()->print_cr("--------------------------------------------------------------------------------");

View file

@ -100,11 +100,12 @@ class Disassembler : public AbstractDisassembler {
} }
// Directly disassemble code blob. // Directly disassemble code blob.
static void decode(CodeBlob *cb, outputStream* st = NULL); static void decode(CodeBlob* cb, outputStream* st = NULL);
// Directly disassemble nmethod. // Directly disassemble nmethod.
static void decode(nmethod* nm, outputStream* st = NULL); static void decode(nmethod* nm, outputStream* st = NULL);
// Disassemble an arbitrary memory range. // Disassemble an arbitrary memory range.
static void decode(address start, address end, outputStream* st = NULL, const CodeStrings* = NULL); static void decode(address start, address end, outputStream* st = NULL
NOT_PRODUCT(COMMA const AsmRemarks* remarks = NULL COMMA ptrdiff_t disp = 0));
static void _hook(const char* file, int line, class MacroAssembler* masm); static void _hook(const char* file, int line, class MacroAssembler* masm);

View file

@ -47,18 +47,21 @@
# define __ _masm-> # define __ _masm->
//------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Implementation of InterpreterCodelet // Implementation of InterpreterCodelet
void InterpreterCodelet::initialize(const char* description, Bytecodes::Code bytecode) { void InterpreterCodelet::initialize(const char* description, Bytecodes::Code bytecode) {
_description = description; _description = description;
_bytecode = bytecode; _bytecode = bytecode;
} #ifndef PRODUCT
AsmRemarks* arp = new(&_asm_remarks) AsmRemarks();
DbgStrings* dsp = new(&_dbg_strings) DbgStrings();
void InterpreterCodelet::verify() { postcond(arp == &_asm_remarks);
postcond(dsp == &_dbg_strings);
#endif
} }
void InterpreterCodelet::verify() {}
void InterpreterCodelet::print_on(outputStream* st) const { void InterpreterCodelet::print_on(outputStream* st) const {
ttyLocker ttyl; ttyLocker ttyl;
@ -75,7 +78,7 @@ void InterpreterCodelet::print_on(outputStream* st) const {
if (PrintInterpreter) { if (PrintInterpreter) {
st->cr(); st->cr();
Disassembler::decode(code_begin(), code_end(), st DEBUG_ONLY(COMMA &_strings)); Disassembler::decode(code_begin(), code_end(), st NOT_PRODUCT(COMMA &_asm_remarks));
} }
} }
@ -104,9 +107,13 @@ CodeletMark::~CodeletMark() {
// Commit Codelet. // Commit Codelet.
int committed_code_size = (*_masm)->code()->pure_insts_size(); int committed_code_size = (*_masm)->code()->pure_insts_size();
if (committed_code_size) { if (committed_code_size > 0) {
CodeStrings cs NOT_PRODUCT(= (*_masm)->code()->strings()); // This is the ONE place where we pickup any assembly remarks and debug
AbstractInterpreter::code()->commit(committed_code_size, cs); // strings, and propagate these to the codelet.
NOT_PRODUCT(_clet->use_remarks((*_masm)->code()->asm_remarks()));
NOT_PRODUCT(_clet->use_strings((*_masm)->code()->dbg_strings()));
AbstractInterpreter::code()->commit(committed_code_size);
} }
// Make sure nobody can use _masm outside a CodeletMark lifespan. // Make sure nobody can use _masm outside a CodeletMark lifespan.
*_masm = NULL; *_masm = NULL;

View file

@ -46,17 +46,15 @@ class InterpreterCodelet: public Stub {
friend class VMStructs; friend class VMStructs;
friend class CodeCacheDumper; // possible extension [do not remove] friend class CodeCacheDumper; // possible extension [do not remove]
private: private:
int _size; // the size in bytes int _size; // the size in bytes
const char* _description; // a description of the codelet, for debugging & printing const char* _description; // a description of the codelet, for debugging & printing
Bytecodes::Code _bytecode; // associated bytecode if any Bytecodes::Code _bytecode; // associated bytecode if any
NOT_PRODUCT(CodeStrings _strings;) // Comments for annotating assembler output. NOT_PRODUCT(AsmRemarks _asm_remarks;) // Comments for annotating assembler output.
NOT_PRODUCT(DbgStrings _dbg_strings;) // Debug strings used in generated code.
public: public:
// Initialization/finalization // Initialization/finalization
void initialize(int size, void initialize(int size) { _size = size; }
CodeStrings& strings) { _size = size;
NOT_PRODUCT(_strings = CodeStrings();)
NOT_PRODUCT(_strings.copy(strings);) }
void finalize() { ShouldNotCallThis(); } void finalize() { ShouldNotCallThis(); }
// General info/converters // General info/converters
@ -79,6 +77,15 @@ class InterpreterCodelet: public Stub {
int code_size() const { return code_end() - code_begin(); } int code_size() const { return code_end() - code_begin(); }
const char* description() const { return _description; } const char* description() const { return _description; }
Bytecodes::Code bytecode() const { return _bytecode; } Bytecodes::Code bytecode() const { return _bytecode; }
#ifndef PRODUCT
~InterpreterCodelet() {
// InterpreterCodelets reside in the StubQueue and should not be deleted,
// nor are they ever finalized (see above).
ShouldNotCallThis();
}
void use_remarks(AsmRemarks &remarks) { _asm_remarks.share(remarks); }
void use_strings(DbgStrings &strings) { _dbg_strings.share(strings); }
#endif
}; };
// Define a prototype interface // Define a prototype interface

View file

@ -1320,7 +1320,8 @@ void SignatureHandlerLibrary::add(const methodHandle& method) {
fingerprint, fingerprint,
buffer.insts_size()); buffer.insts_size());
if (buffer.insts_size() > 0) { if (buffer.insts_size() > 0) {
Disassembler::decode(handler, handler + buffer.insts_size()); Disassembler::decode(handler, handler + buffer.insts_size(), tty
NOT_PRODUCT(COMMA &buffer.asm_remarks()));
} }
#ifndef PRODUCT #ifndef PRODUCT
address rh_begin = Interpreter::result_handler(method()->result_type()); address rh_begin = Interpreter::result_handler(method()->result_type());

View file

@ -2923,7 +2923,8 @@ AdapterHandlerEntry* AdapterHandlerLibrary::create_adapter(AdapterBlob*& new_ada
if (Verbose || PrintStubCode) { if (Verbose || PrintStubCode) {
address first_pc = entry->base_address(); address first_pc = entry->base_address();
if (first_pc != NULL) { if (first_pc != NULL) {
Disassembler::decode(first_pc, first_pc + insts_size); Disassembler::decode(first_pc, first_pc + insts_size, tty
NOT_PRODUCT(COMMA &new_adapter->asm_remarks()));
tty->cr(); tty->cr();
} }
} }

View file

@ -78,7 +78,8 @@ StubCodeGenerator::~StubCodeGenerator() {
CodeBuffer* cbuf = _masm->code(); CodeBuffer* cbuf = _masm->code();
CodeBlob* blob = CodeCache::find_blob_unsafe(cbuf->insts()->start()); CodeBlob* blob = CodeCache::find_blob_unsafe(cbuf->insts()->start());
if (blob != NULL) { if (blob != NULL) {
blob->set_strings(cbuf->strings()); blob->use_remarks(cbuf->asm_remarks());
blob->use_strings(cbuf->dbg_strings());
} }
#endif #endif
} }
@ -90,15 +91,15 @@ void StubCodeGenerator::stub_prolog(StubCodeDesc* cdesc) {
void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) { void StubCodeGenerator::stub_epilog(StubCodeDesc* cdesc) {
if (_print_code) { if (_print_code) {
#ifndef PRODUCT #ifndef PRODUCT
// Find the code strings in the outer CodeBuffer. // Find the assembly code remarks in the outer CodeBuffer.
CodeBuffer *outer_cbuf = _masm->code_section()->outer(); AsmRemarks* remarks = &_masm->code_section()->outer()->asm_remarks();
CodeStrings* cs = &outer_cbuf->strings();
#endif #endif
ttyLocker ttyl; ttyLocker ttyl;
tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("- - - [BEGIN] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
cdesc->print_on(tty); cdesc->print_on(tty);
tty->cr(); tty->cr();
Disassembler::decode(cdesc->begin(), cdesc->end(), tty NOT_PRODUCT(COMMA cs)); Disassembler::decode(cdesc->begin(), cdesc->end(), tty
NOT_PRODUCT(COMMA remarks COMMA cdesc->disp()));
tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"); tty->print_cr("- - - [END] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
tty->cr(); tty->cr();
} }
@ -119,6 +120,11 @@ StubCodeMark::~StubCodeMark() {
_cgen->assembler()->flush(); _cgen->assembler()->flush();
_cdesc->set_end(_cgen->assembler()->pc()); _cdesc->set_end(_cgen->assembler()->pc());
assert(StubCodeDesc::_list == _cdesc, "expected order on list"); assert(StubCodeDesc::_list == _cdesc, "expected order on list");
#ifndef PRODUCT
address base = _cgen->assembler()->code_section()->outer()->insts_begin();
address head = _cdesc->begin();
_cdesc->set_disp(uint(head - base));
#endif
_cgen->stub_epilog(_cdesc); _cgen->stub_epilog(_cdesc);
Forte::register_stub(_cdesc->name(), _cdesc->begin(), _cdesc->end()); Forte::register_stub(_cdesc->name(), _cdesc->begin(), _cdesc->end());

View file

@ -38,19 +38,18 @@
class StubCodeDesc: public CHeapObj<mtCode> { class StubCodeDesc: public CHeapObj<mtCode> {
private: private:
static StubCodeDesc* _list; // the list of all descriptors static StubCodeDesc* _list; // the list of all descriptors
static bool _frozen; // determines whether _list modifications are allowed static bool _frozen; // determines whether _list modifications are allowed
StubCodeDesc* _next; // the next element in the linked list StubCodeDesc* _next; // the next element in the linked list
const char* _group; // the group to which the stub code belongs const char* _group; // the group to which the stub code belongs
const char* _name; // the name assigned to the stub code const char* _name; // the name assigned to the stub code
address _begin; // points to the first byte of the stub code (included) address _begin; // points to the first byte of the stub code (included)
address _end; // points to the first byte after the stub code (excluded) address _end; // points to the first byte after the stub code (excluded)
uint _disp; // Displacement relative base address in buffer.
void set_end(address end) { friend class StubCodeMark;
assert(_begin <= end, "begin & end not properly ordered"); friend class StubCodeGenerator;
_end = end;
}
void set_begin(address begin) { void set_begin(address begin) {
assert(begin >= _begin, "begin may not decrease"); assert(begin >= _begin, "begin may not decrease");
@ -58,8 +57,12 @@ class StubCodeDesc: public CHeapObj<mtCode> {
_begin = begin; _begin = begin;
} }
friend class StubCodeMark; void set_end(address end) {
friend class StubCodeGenerator; assert(_begin <= end, "begin & end not properly ordered");
_end = end;
}
void set_disp(uint disp) { _disp = disp; }
public: public:
static StubCodeDesc* first() { return _list; } static StubCodeDesc* first() { return _list; }
@ -76,6 +79,7 @@ class StubCodeDesc: public CHeapObj<mtCode> {
_name = name; _name = name;
_begin = begin; _begin = begin;
_end = end; _end = end;
_disp = 0;
_list = this; _list = this;
}; };
@ -85,6 +89,7 @@ class StubCodeDesc: public CHeapObj<mtCode> {
const char* name() const { return _name; } const char* name() const { return _name; }
address begin() const { return _begin; } address begin() const { return _begin; }
address end() const { return _end; } address end() const { return _end; }
uint disp() const { return _disp; }
int size_in_bytes() const { return _end - _begin; } int size_in_bytes() const { return _end - _begin; }
bool contains(address pc) const { return _begin <= pc && pc < _end; } bool contains(address pc) const { return _begin <= pc && pc < _end; }
void print_on(outputStream* st) const; void print_on(outputStream* st) const;

View file

@ -0,0 +1,263 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#ifndef PRODUCT
#ifndef ZERO
#include "asm/macroAssembler.hpp"
#include "compiler/disassembler.hpp"
#include "memory/resourceArea.hpp"
#include "unittest.hpp"
#include <regex>
static const char* replace_addr_expr(const char* str)
{
// Remove any address expression "0x0123456789abcdef" found in order to
// aid string comparison. Also remove any trailing printout from a padded
// buffer.
std::basic_string<char> tmp = std::regex_replace(str, std::regex("0x[0-9a-fA-F]+"), "<addr>");
std::basic_string<char> red = std::regex_replace(tmp, std::regex("\\s+<addr>:\\s+\\.inst\\t<addr> ; undefined"), "");
return os::strdup(red.c_str());
}
static const char* delete_header_line(const char* str)
{
// Remove (second) header line in output, e.g.:
// Decoding CodeBlob, name: CodeStringTest, at [<addr>, <addr>] 8 bytes\n
std::basic_string<char> red = std::regex_replace(str, std::regex("Decoding.+bytes\\n"), "");
return os::strdup(red.c_str());
}
static void asm_remarks_check(const AsmRemarks &rem1,
const AsmRemarks &rem2)
{
ASSERT_EQ(rem1.ref(), rem2.ref()) << "Should share the same collection.";
}
static void dbg_strings_check(const DbgStrings &dbg1,
const DbgStrings &dbg2)
{
ASSERT_EQ(dbg1.ref(), dbg2.ref()) << "Should share the same collection.";
}
static void disasm_string_check(CodeBuffer* cbuf, CodeBlob* blob)
{
if (Disassembler::is_abstract())
{
return; // No disassembler available (no comments will be used).
}
stringStream out1, out2;
Disassembler::decode(cbuf->insts_begin(), cbuf->insts_end(), &out1, &cbuf->asm_remarks());
Disassembler::decode(blob->code_begin(), blob->code_end(), &out2, &blob->asm_remarks());
EXPECT_STREQ(replace_addr_expr(out1.as_string()),
replace_addr_expr(out2.as_string()))
<< "1. Output should be identical.";
stringStream out3;
Disassembler::decode(blob, &out3);
EXPECT_STREQ(replace_addr_expr(out2.as_string()),
replace_addr_expr(delete_header_line(out3.as_string())))
<< "2. Output should be identical.";
}
static void copy_and_compare(CodeBuffer* cbuf)
{
bool remarks_empty = cbuf->asm_remarks().is_empty();
bool strings_empty = cbuf->dbg_strings().is_empty();
BufferBlob* blob = BufferBlob::create("CodeBuffer Copy&Compare", cbuf);
// 1. Check Assembly Remarks are shared by buffer and blob.
asm_remarks_check(cbuf->asm_remarks(), blob->asm_remarks());
// 2. Check Debug Strings are shared by buffer and blob.
dbg_strings_check(cbuf->dbg_strings(), blob->dbg_strings());
// 3. Check that the disassembly output matches.
disasm_string_check(cbuf, blob);
BufferBlob::free(blob);
ASSERT_EQ(remarks_empty, cbuf->asm_remarks().is_empty())
<< "Expecting property to be unchanged.";
ASSERT_EQ(strings_empty, cbuf->dbg_strings().is_empty())
<< "Expecting property to be unchanged.";
}
static void code_buffer_test()
{
constexpr int BUF_SZ = 256;
ResourceMark rm;
CodeBuffer cbuf("CodeStringTest", BUF_SZ, BUF_SZ);
MacroAssembler as(&cbuf);
ASSERT_TRUE(cbuf.asm_remarks().is_empty());
ASSERT_TRUE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
int re, sz, n;
re = cbuf.insts_remaining();
// 1. Generate a first entry.
as.block_comment("First block comment.");
as.nop();
sz = re - cbuf.insts_remaining();
ASSERT_TRUE(sz > 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_TRUE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
copy_and_compare(&cbuf);
n = re/sz;
ASSERT_TRUE(n > 0);
// 2. Generate additional entries without causing the buffer to expand.
for (unsigned i = 0; i < unsigned(n)/2; i++)
{
ASSERT_FALSE(cbuf.insts()->maybe_expand_to_ensure_remaining(sz));
ASSERT_TRUE(cbuf.insts_remaining()/sz >= n/2);
stringStream strm;
strm.print("Comment No. %d", i);
as.block_comment(strm.as_string());
as.nop();
}
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
copy_and_compare(&cbuf);
re = cbuf.insts_remaining();
// 3. Generate a single code with a debug string.
as.unimplemented("First debug string.");
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
sz = re - cbuf.insts_remaining();
n = (re - sz)/sz;
ASSERT_TRUE(n > 0);
// 4. Generate additional code with debug strings.
for (unsigned i = 0; i < unsigned(n); i++)
{
ASSERT_TRUE(cbuf.insts_remaining() >= sz);
stringStream strm;
strm.print("Fixed address string No. %d", i);
as.unimplemented(strm.as_string());
}
ASSERT_TRUE(cbuf.insts_remaining() >= 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
ASSERT_TRUE(cbuf.blob()->asm_remarks().is_empty());
ASSERT_TRUE(cbuf.blob()->dbg_strings().is_empty());
copy_and_compare(&cbuf);
}
static void buffer_blob_test()
{
constexpr int BUF_SZ = 256;
ResourceMark rm;
BufferBlob* blob = BufferBlob::create("BufferBlob Test", BUF_SZ);
CodeBuffer cbuf(blob);
MacroAssembler as(&cbuf);
ASSERT_FALSE(cbuf.insts()->has_locs());
// The x86-64 version of 'stop' will use relocation info. that will result
// in tainting the location start and limit if no location info. buffer is
// present.
static uint8_t s_loc_buf[BUF_SZ]; // Raw memory buffer used for relocInfo.
cbuf.insts()->initialize_shared_locs((relocInfo*)&s_loc_buf[0], BUF_SZ);
int re = cbuf.insts_remaining();
as.block_comment("First block comment.");
as.nop();
as.unimplemented("First debug string.");
int sz = re - cbuf.insts_remaining();
ASSERT_TRUE(sz > 0);
constexpr int LIM_GEN = 51; // Limit number of entries generated.
for (unsigned i = 0; i < LIM_GEN; i++)
{
if (cbuf.insts_remaining() < sz) break;
stringStream strm1;
strm1.print("Comment No. %d", i);
as.block_comment(strm1.as_string());
as.nop();
stringStream strm2;
strm2.print("Fixed address string No. %d", i);
as.unimplemented(strm2.as_string());
}
ASSERT_TRUE(cbuf.insts_remaining() >= 0);
ASSERT_FALSE(cbuf.asm_remarks().is_empty());
ASSERT_FALSE(cbuf.dbg_strings().is_empty());
copy_and_compare(&cbuf);
ASSERT_TRUE(blob->asm_remarks().is_empty());
ASSERT_TRUE(blob->dbg_strings().is_empty());
BufferBlob::free(blob);
}
TEST_VM(codestrings, validate)
{
code_buffer_test();
buffer_blob_test();
}
#endif // not ZERO
#endif // not PRODUCT