Commit 2048e5b7 authored by Artem Serov's avatar Artem Serov Committed by Commit Bot

[turbofan] Improve load poisoning tests.

Introduce a helper class for regular expression parsing
and use it to improve load poison tests readability and
maintainability.

Extend load poisoning tests for arm64 platform (e.g.
for both regular and compressed references cases).

Change-Id: Ie62dfd14a60186feaa5f48e1a6122d77766472af
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1645913
Commit-Queue: Martyn Capewell <martyn.capewell@arm.com>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62493}
parent 2fe2a08b
......@@ -109,6 +109,8 @@ v8_source_set("cctest_sources") {
"compiler/test-run-variables.cc",
"compiler/value-helper.cc",
"compiler/value-helper.h",
"disasm-regex-helper.cc",
"disasm-regex-helper.h",
"expression-type-collector-macros.h",
"gay-fixed.cc",
"gay-fixed.h",
......@@ -307,6 +309,7 @@ v8_source_set("cctest_sources") {
"test-javascript-arm64.cc",
"test-js-arm64-variables.cc",
"test-macro-assembler-arm64.cc",
"test-poison-disasm-arm64.cc",
"test-sync-primitives-arm64.cc",
"test-utils-arm64.cc",
"test-utils-arm64.h",
......
// Copyright 2019 the V8 project 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 "test/cctest/disasm-regex-helper.h"
#include "src/api/api-inl.h"
#include "src/diagnostics/disassembler.h"
#include "src/objects/objects-inl.h"
#include "test/cctest/cctest.h"
namespace v8 {
namespace internal {
namespace {
std::string DisassembleFunction(const char* function) {
v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
Handle<JSFunction> f = Handle<JSFunction>::cast(
v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast(
CcTest::global()->Get(context, v8_str(function)).ToLocalChecked())));
Address begin = f->code().raw_instruction_start();
Address end = f->code().raw_instruction_end();
Isolate* isolate = CcTest::i_isolate();
std::ostringstream os;
Disassembler::Decode(isolate, &os, reinterpret_cast<byte*>(begin),
reinterpret_cast<byte*>(end),
CodeReference(handle(f->code(), isolate)));
return os.str();
}
} // namespace
bool CheckDisassemblyRegexPatterns(
const char* function_name, const std::vector<std::string>& patterns_array) {
std::istringstream reader(DisassembleFunction(function_name));
size_t size = patterns_array.size();
DCHECK_GT(size, 0);
std::smatch match;
std::string line;
RegexParser parser;
const std::string& first_pattern = patterns_array[0];
while (std::getline(reader, line)) {
RegexParser::Status status = parser.ProcessPattern(line, first_pattern);
if (status == RegexParser::Status::kSuccess) {
CHECK(std::getline(reader, line));
for (size_t i = 1; i < size; i++) {
const std::string& pattern = patterns_array[i];
status = parser.ProcessPattern(line, pattern);
if (status != RegexParser::Status::kSuccess) {
std::cout << "Pattern \"" << pattern << "\" not found" << std::endl;
std::cout << "Line: \"" << line << "\":" << std::endl;
parser.PrintSymbols(std::cout);
return false;
}
CHECK(std::getline(reader, line));
}
return true;
}
}
return false;
}
namespace {
void RegexCheck(
const std::vector<std::string>& inputs,
const std::vector<std::string>& patterns,
RegexParser::Status expected_status,
std::function<void(const RegexParser&)> func = [](const RegexParser&) {}) {
size_t size = patterns.size();
CHECK_EQ(size, inputs.size());
RegexParser parser;
RegexParser::Status status;
size_t i = 0;
for (; i < size - 1; i++) {
const std::string& line = inputs[i];
const std::string& pattern = patterns[i];
status = parser.ProcessPattern(line, pattern);
CHECK_EQ(status, RegexParser::Status::kSuccess);
}
const std::string& line = inputs[i];
const std::string& pattern = patterns[i];
status = parser.ProcessPattern(line, pattern);
if (status != expected_status) {
parser.PrintSymbols(std::cout);
}
CHECK_EQ(status, expected_status);
func(parser);
}
// Check a line against a pattern.
void RegexCheckOne(
const std::string& line, const std::string& pattern,
RegexParser::Status expected_status,
std::function<void(const RegexParser&)> func = [](const RegexParser&) {}) {
RegexParser parser;
RegexParser::Status status = parser.ProcessPattern(line, pattern);
CHECK_EQ(status, expected_status);
func(parser);
}
void TestSymbolValue(const std::string& sym_name, const std::string& value,
const RegexParser& p) {
CHECK(p.IsSymbolDefined(sym_name));
CHECK_EQ(p.GetSymbolMatchedValue(sym_name).compare(value), 0);
}
} // namespace
// clang-format off
TEST(RegexParserSingleLines) {
//
// Simple one-liners for found/not found.
//
RegexCheckOne(" a b a b c a",
"a b c",
RegexParser::Status::kSuccess);
RegexCheckOne(" a b a bc a",
"a b c",
RegexParser::Status::kNotMatched);
RegexCheckOne("aaabbaaa",
"ab.*?a",
RegexParser::Status::kSuccess);
RegexCheckOne("aaabbaa",
"^(?:aa+|b)+$",
RegexParser::Status::kSuccess);
RegexCheckOne("aaabba",
"^(?:aa+|b)+$",
RegexParser::Status::kNotMatched);
RegexCheckOne("(aaa)",
"\\(a+\\)",
RegexParser::Status::kSuccess);
RegexCheckOne("r19 qwerty",
"r<<Def:[0-9]+>>",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def", "19", p);
});
RegexCheckOne("r19 qwerty",
"r<<Def:[a-z]+>>",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def", "ty", p);
});
// Backreference/submatch groups are forbidden.
RegexCheckOne("aaabba",
"((aa+)|b)+?",
RegexParser::Status::kWrongPattern);
// Using passive groups.
RegexCheckOne("aaabba",
"(?:(?:aa+)|b)+?",
RegexParser::Status::kSuccess);
//
// Symbol definitions.
//
RegexCheckOne("r19 r20",
"r<<Def:19>>",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def", "19", p);
});
RegexCheckOne("r19 r20",
"r<<Def:[0-9]+>>",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def", "19", p);
});
RegexCheckOne("r19 r20",
"r<<Def0:[0-9]+>>.*?r<<Def1:[0-9]+>>",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def0", "19", p);
TestSymbolValue("Def1", "20", p);
});
RegexCheckOne("r19 r20",
"r<<Def0:[0-9]+>>.*?r[0-9]",
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def0", "19", p);
});
// Checks that definitions are not committed unless the pattern is matched.
RegexCheckOne("r19",
"r<<Def0:[0-9]+>>.*?r<<Def1:[0-9]+>>",
RegexParser::Status::kNotMatched,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Def0"));
CHECK(!p.IsSymbolDefined("Def1"));
});
RegexCheckOne("r19 r19 r1",
"r<<Def0:[0-9]+>>.*?r<<Def0:[0-9]+>> r<<Def1:[0-9]+>>",
RegexParser::Status::kRedefinition,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Def0"));
CHECK(!p.IsSymbolDefined("Def1"));
});
RegexCheckOne("r19 r1",
"r<<Def0:[0-9]+>> (r1)",
RegexParser::Status::kWrongPattern,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Def0"));
});
//
// Undefined symbol references.
//
RegexCheckOne("r19 r1",
"r[0-9].*?r<<Undef>>",
RegexParser::Status::kDefNotFound,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Undef"));
});
RegexCheckOne("r19 r1",
"r<<Def0:[0-9]+>>.*?<<Undef>>",
RegexParser::Status::kDefNotFound,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Undef"));
CHECK(!p.IsSymbolDefined("Def0"));
});
RegexCheckOne("r19 r19",
"r<<Def0:[0-9]+>>.*?<<Def0>>",
RegexParser::Status::kDefNotFound,
[] (const RegexParser& p) {
CHECK(!p.IsSymbolDefined("Def0"));
});
}
TEST(RegexParserMultiLines) {
RegexCheck({ " a b a b c a",
" a b a b c a" },
{ "a b c",
"a b c" },
RegexParser::Status::kSuccess);
RegexCheck({ "r16 = r15",
"r17 = r16" },
{ "<<Def:r[0-9]+>> = r[0-9]+",
"[0-9]+ = <<Def>>" },
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def", "r16", p);
});
RegexCheck({ "r16 = r15 + r13",
"r17 = r16 + r14",
"r19 = r14" },
{ "<<Def0:r[0-9]+>> = r[0-9]+",
"<<Def1:r[0-9]+>> = <<Def0>> \\+ <<Def2:r[0-9]+>>",
"<<Def3:r[0-9]+>> = <<Def2>>" },
RegexParser::Status::kSuccess,
[] (const RegexParser& p) {
TestSymbolValue("Def0", "r16", p);
TestSymbolValue("Def1", "r17", p);
TestSymbolValue("Def2", "r14", p);
TestSymbolValue("Def3", "r19", p);
});
// Constraint is not met for Def (r19 != r16).
RegexCheck({ "r16 = r15",
"r17 = r19" },
{ "<<Def:r[0-9]+>> = r[0-9]+",
"[0-9]+ = <<Def>>" },
RegexParser::Status::kNotMatched,
[] (const RegexParser& p) {
TestSymbolValue("Def", "r16", p);
});
}
// clang-format on
} // namespace internal
} // namespace v8
This diff is collapsed.
This diff is collapsed.
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The C++ style guide recommends using <re2> instead of <regex>. However, the
// former isn't available in V8.
#include <regex> // NOLINT(build/c++11)
#include <vector>
#include "src/codegen/arm64/register-arm64.h"
#include "test/cctest/cctest.h"
#include "test/cctest/disasm-regex-helper.h"
namespace v8 {
namespace internal {
namespace {
// Poison register.
const int kPRegCode = kSpeculationPoisonRegister.code();
const std::string kPReg = // NOLINT(runtime/string)
"x" + std::to_string(kPRegCode);
} // namespace
TEST(DisasmPoisonMonomorphicLoad) {
#ifdef ENABLE_DISASSEMBLER
if (i::FLAG_always_opt || !i::FLAG_opt) return;
i::FLAG_allow_natives_syntax = true;
i::FLAG_untrusted_code_mitigations = true;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
CompileRun(
"function mono(o) { return o.x; };"
"%PrepareFunctionForOptimization(mono);"
"mono({ x : 1 });"
"mono({ x : 1 });"
"%OptimizeFunctionOnNextCall(mono);"
"mono({ x : 1 });");
// Matches that the property access sequence is instrumented with
// poisoning.
#if defined(V8_COMPRESS_POINTERS)
std::vector<std::string> patterns_array = {
"ldur <<Map:w[0-9]+>>, \\[<<Obj:x[0-9]+>>, #-1\\]", // load map
"ldr <<ExpMap:w[0-9]+>>, pc", // load expected map
"cmp <<Map>>, <<ExpMap>>", // compare maps
"b.ne", // deopt if different
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur w<<Field:[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load the field
"and x<<Field>>, x<<Field>>, " + kPReg, // apply the poison
};
#else
std::vector<std::string> patterns_array = {
"ldur <<Map:x[0-9]+>>, \\[<<Obj:x[0-9]+>>, #-1\\]", // load map
"ldr <<ExpMap:x[0-9]+>>, pc", // load expected map
"cmp <<Map>>, <<ExpMap>>", // compare maps
"b.ne", // deopt if different
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur <<Field:x[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load the field
"and <<Field>>, <<Field>>, " + kPReg, // apply the poison
};
#endif
CHECK(CheckDisassemblyRegexPatterns("mono", patterns_array));
#endif // ENABLE_DISASSEMBLER
}
TEST(DisasmPoisonPolymorphicLoad) {
#ifdef ENABLE_DISASSEMBLER
if (i::FLAG_always_opt || !i::FLAG_opt) return;
i::FLAG_allow_natives_syntax = true;
i::FLAG_untrusted_code_mitigations = true;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
CompileRun(
"function poly(o) { return o.x + 1; };"
"let o1 = { x : 1 };"
"let o2 = { y : 1 };"
"o2.x = 2;"
"%PrepareFunctionForOptimization(poly);"
"poly(o1);"
"poly(o2);"
"poly(o1);"
"poly(o2);"
"%OptimizeFunctionOnNextCall(poly);"
"poly(o1);");
// Matches that the property access sequence is instrumented with
// poisoning.
#if defined(V8_COMPRESS_POINTERS)
std::vector<std::string> patterns_array = {
"ldur <<Map0:w[0-9]+>>, \\[<<Obj:x[0-9]+>>, #-1\\]", // load map
"ldr <<ExpMap:w[0-9]+>>, pc", // load map const #1
"cmp <<Map0>>, <<ExpMap>>", // compare maps
"b.eq", // ? go to the load
"csel " + kPReg + ", xzr, " + kPReg + ", eq", // update the poison
"csdb", // spec. barrier
"ldur <<Map1:w[0-9]+>>, \\[<<Obj>>, #-1\\]", // load map
"ldr <<ExpMap1:w[0-9]+>>, pc", // load map const #2
"cmp <<Map1>>, <<ExpMap1>>", // compare maps
"b.ne", // deopt if different
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur w<<Field:[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load the field
"and x<<Field>>, x<<Field>>, " + kPReg, // apply the poison
"sxtw x<<Field>>, w<<Field>>",
"asr w[0-9]+, w<<Field>>, #1", // untag
"b", // goto merge point
// Lcase1:
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur w<<BSt:[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load backing store
"and x<<BSt>>, x<<BSt>>, " + kPReg, // apply the poison
"sbfx <<Temp:x[0-9]+>>, x<<BSt>>, #0, #1", // Decompress ref
"and <<Temp>>, <<Temp>>, x26", // Decompress ref
"add x<<BSt>>, <<Temp>>, w<<BSt>>, sxtw", // Decompress ref
"ldur w<<Prop:[0-9]+>>, \\[x<<BSt>>, #[0-9]+\\]", // load the property
"and x<<Prop>>, x<<Prop>>, " + kPReg, // apply the poison
// Ldone:
};
#else
std::vector<std::string> patterns_array = {
"ldur <<Map0:x[0-9]+>>, \\[<<Obj:x[0-9]+>>, #-1\\]", // load map
"ldr <<ExpMap0:x[0-9]+>>, pc", // load map const #1
"cmp <<Map0>>, <<ExpMap0>>", // compare maps
"b.eq", // ? go to the load
"csel " + kPReg + ", xzr, " + kPReg + ", eq", // update the poison
"csdb", // spec. barrier
"ldur <<Map1:x[0-9]+>>, \\[<<Obj>>, #-1\\]", // load map
"ldr <<ExpMap1:x[0-9]+>>, pc", // load map const #2
"cmp <<Map1>>, <<ExpMap1>>", // compare maps
"b.ne", // deopt if different
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur <<Field:x[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load the field
"and <<Field>>, <<Field>>, " + kPReg, // apply the poison
"asr x[0-9]+, <<Field>>, #32", // untag
"b", // goto merge point
// Lcase1:
"csel " + kPReg + ", xzr, " + kPReg + ", ne", // update the poison
"csdb", // spec. barrier
"ldur <<BSt:x[0-9]+>>, \\[<<Obj>>, #[0-9]+\\]", // load backing store
"and <<BSt>>, <<BSt>>, " + kPReg, // apply the poison
"ldur <<Prop:x[0-9]+>>, \\[<<BSt>>, #[0-9]+\\]", // load the property
"and <<Prop>>, <<Prop>>, " + kPReg, // apply the poison
// Ldone:
};
#endif
CHECK(CheckDisassemblyRegexPatterns("poly", patterns_array));
#endif // ENABLE_DISASSEMBLER
}
} // namespace internal
} // namespace v8
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