7dbd03388e
Reviewed-by: thartmann, chagedorn
279 lines
8.6 KiB
C++
279 lines
8.6 KiB
C++
/*
|
|
* Copyright (c) 2021, 2022, 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.inline.hpp"
|
|
#include "compiler/disassembler.hpp"
|
|
#include "memory/resourceArea.hpp"
|
|
|
|
#include "utilities/vmassert_uninstall.hpp"
|
|
#include <regex>
|
|
#include "utilities/vmassert_reinstall.hpp"
|
|
|
|
#include "unittest.hpp"
|
|
|
|
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 (too brittle?).
|
|
|
|
std::basic_string<char> tmp1 = std::regex_replace(str, std::regex("0x[0-9a-fA-F]+"), "<addr>");
|
|
// Padding: aarch64
|
|
std::basic_string<char> tmp2 = std::regex_replace(tmp1, std::regex("\\s+<addr>:\\s+\\.inst\\t<addr> ; undefined"), "");
|
|
std::basic_string<char> tmp3 = std::regex_replace(tmp2, std::regex("\\s+<addr>:\\s+udf\\t#0"), "");
|
|
// Padding: riscv
|
|
std::basic_string<char> tmp4 = std::regex_replace(tmp3, std::regex("\\s+<addr>:\\s+unimp"), "");
|
|
// Padding: x64
|
|
std::basic_string<char> tmp5 = std::regex_replace(tmp4, std::regex("\\s+<addr>:\\s+hlt[ \\t]+(?!\\n\\s+;;)"), "");
|
|
std::basic_string<char> red = std::regex_replace(tmp5, std::regex("(\\s+<addr>:\\s+nop)[ \\t]*"), "$1");
|
|
|
|
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);
|
|
}
|
|
|
|
#if defined(PPC) || defined(S390)
|
|
// Neither ppc nor s390 compiler use code strings
|
|
TEST_VM(codestrings, DISABLED_validate)
|
|
#else
|
|
TEST_VM(codestrings, validate)
|
|
#endif
|
|
{
|
|
code_buffer_test();
|
|
buffer_blob_test();
|
|
}
|
|
|
|
#endif // not ZERO
|
|
#endif // not PRODUCT
|