Commit 3bb899eb authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

[v8windbg] Generate debug macros files

Docs: https://docs.google.com/document/d/13n1qaB6A-gvgWc9NDhWm-UPuOqow_Y0DNgCeTbtIotI

Modify that C++ backend so that it can emit either runtime C++ or
postmortem debugging code. When in postmortem debugging mode, the
overall code structure would look similar with some difference:
1. Instead of passing an Isolate* everywhere, we pass a MemoryAccessor.
2. Instead of runtime class names like String, we use uintptr_t
3. When loading data from objects, instead of TaggedField<T>::load or
Object::ReadField (which read from the current process), we use the
MemoryAccessor and read data from the debuggee process.
4. Return values should be wrapped in the Value struct.

Implement the debug accessors for complex length expressions and add
test for such class (SmallOrderedHashSet).

Change-Id: I34107c92b31ed4e07bb628ae58c84487e41ba648
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2477921
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSeth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#72148}
parent 6ada6a90
......@@ -1445,22 +1445,24 @@ template("run_torque") {
files = [
"$target_gen_dir/torque-generated/bit-fields.h",
"$target_gen_dir/torque-generated/builtin-definitions.h",
"$target_gen_dir/torque-generated/interface-descriptors.inc",
"$target_gen_dir/torque-generated/factory.cc",
"$target_gen_dir/torque-generated/factory.inc",
"$target_gen_dir/torque-generated/field-offsets.h",
"$target_gen_dir/torque-generated/class-debug-readers.cc",
"$target_gen_dir/torque-generated/class-debug-readers.h",
"$target_gen_dir/torque-generated/class-forward-declarations.h",
"$target_gen_dir/torque-generated/class-verifiers.cc",
"$target_gen_dir/torque-generated/class-verifiers.h",
"$target_gen_dir/torque-generated/csa-types.h",
"$target_gen_dir/torque-generated/debug-macros.cc",
"$target_gen_dir/torque-generated/debug-macros.h",
"$target_gen_dir/torque-generated/enum-verifiers.cc",
"$target_gen_dir/torque-generated/objects-printer.cc",
"$target_gen_dir/torque-generated/objects-body-descriptors-inl.inc",
"$target_gen_dir/torque-generated/class-debug-readers.cc",
"$target_gen_dir/torque-generated/class-debug-readers.h",
"$target_gen_dir/torque-generated/exported-macros-assembler.cc",
"$target_gen_dir/torque-generated/exported-macros-assembler.h",
"$target_gen_dir/torque-generated/csa-types.h",
"$target_gen_dir/torque-generated/factory.cc",
"$target_gen_dir/torque-generated/factory.inc",
"$target_gen_dir/torque-generated/field-offsets.h",
"$target_gen_dir/torque-generated/instance-types.h",
"$target_gen_dir/torque-generated/class-forward-declarations.h",
"$target_gen_dir/torque-generated/interface-descriptors.inc",
"$target_gen_dir/torque-generated/objects-body-descriptors-inl.inc",
"$target_gen_dir/torque-generated/objects-printer.cc",
]
outputs = []
......
......@@ -74,8 +74,10 @@ Stack<std::string> CCGenerator::EmitBlock(const Block* block) {
const auto& def = block->InputDefinitions().Peek(i);
stack.Push(DefinitionToVariable(def));
if (def.IsPhiFromBlock(block)) {
decls() << " " << block->InputTypes().Peek(i)->GetRuntimeType() << " "
<< stack.Top() << "{}; USE(" << stack.Top() << ");\n";
decls() << " "
<< (is_cc_debug_ ? block->InputTypes().Peek(i)->GetDebugType()
: block->InputTypes().Peek(i)->GetRuntimeType())
<< " " << stack.Top() << "{}; USE(" << stack.Top() << ");\n";
}
}
......@@ -151,8 +153,10 @@ void CCGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
for (std::size_t i = 0; i < lowered.size(); ++i) {
results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
stack->Push(results.back());
decls() << " " << lowered[i]->GetRuntimeType() << " " << stack->Top()
<< "{}; USE(" << stack->Top() << ");\n";
decls() << " "
<< (is_cc_debug_ ? lowered[i]->GetDebugType()
: lowered[i]->GetRuntimeType())
<< " " << stack->Top() << "{}; USE(" << stack->Top() << ");\n";
}
out() << " ";
......@@ -220,29 +224,39 @@ void CCGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
for (std::size_t i = 0; i < lowered.size(); ++i) {
results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
stack->Push(results.back());
decls() << " " << lowered[i]->GetRuntimeType() << " " << stack->Top()
<< "{}; USE(" << stack->Top() << ");\n";
decls() << " "
<< (is_cc_debug_ ? lowered[i]->GetDebugType()
: lowered[i]->GetRuntimeType())
<< " " << stack->Top() << "{}; USE(" << stack->Top() << ");\n";
}
// We should have inlined any calls requiring complex control flow.
CHECK(!instruction.catch_block);
out() << " ";
out() << (is_cc_debug_ ? " ASSIGN_OR_RETURN(" : " ");
if (return_type->StructSupertype().has_value()) {
out() << "std::tie(";
PrintCommaSeparatedList(out(), results);
out() << ") = ";
out() << (is_cc_debug_ ? "), " : ") = ");
} else {
if (results.size() == 1) {
out() << results[0] << " = ";
out() << results[0] << (is_cc_debug_ ? ", " : " = ");
} else {
DCHECK_EQ(0, results.size());
}
}
out() << instruction.macro->CCName() << "(isolate";
if (is_cc_debug_) {
out() << instruction.macro->CCDebugName() << "(accessor";
} else {
out() << instruction.macro->CCName() << "(isolate";
}
if (!args.empty()) out() << ", ";
PrintCommaSeparatedList(out(), args);
out() << ");\n";
if (is_cc_debug_) {
out() << "));\n";
} else {
out() << ");\n";
}
}
void CCGenerator::EmitInstruction(
......@@ -364,16 +378,29 @@ void CCGenerator::EmitInstruction(const LoadReferenceInstruction& instruction,
std::string object = stack->Pop();
stack->Push(result_name);
std::string result_type = instruction.type->GetRuntimeType();
decls() << " " << result_type << " " << result_name << "{}; USE("
<< result_name << ");\n";
out() << " " << result_name << " = ";
if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
out() << "TaggedField<" << result_type << ">::load(isolate, " << object
<< ", static_cast<int>(" << offset << "));\n";
if (!is_cc_debug_) {
std::string result_type = instruction.type->GetRuntimeType();
decls() << " " << result_type << " " << result_name << "{}; USE("
<< result_name << ");\n";
out() << " " << result_name << " = ";
if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
out() << "TaggedField<" << result_type << ">::load(isolate, " << object
<< ", static_cast<int>(" << offset << "));\n";
} else {
out() << "(" << object << ").ReadField<" << result_type << ">(" << offset
<< ");\n";
}
} else {
out() << "(" << object << ").ReadField<" << result_type << ">(" << offset
<< ");\n";
std::string result_type = instruction.type->GetDebugType();
decls() << " " << result_type << " " << result_name << "{}; USE("
<< result_name << ");\n";
if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
out() << " READ_TAGGED_FIELD_OR_FAIL(" << result_name << ", accessor, "
<< object << ", static_cast<int>(" << offset << "));\n";
} else {
out() << " READ_FIELD_OR_FAIL(" << result_type << ", " << result_name
<< ", accessor, " << object << ", " << offset << ");\n";
}
}
}
......
......@@ -13,14 +13,17 @@ namespace torque {
class CCGenerator : public TorqueCodeGenerator {
public:
CCGenerator(const ControlFlowGraph& cfg, std::ostream& out)
: TorqueCodeGenerator(cfg, out) {}
CCGenerator(const ControlFlowGraph& cfg, std::ostream& out,
bool is_cc_debug = false)
: TorqueCodeGenerator(cfg, out), is_cc_debug_(is_cc_debug) {}
base::Optional<Stack<std::string>> EmitGraph(Stack<std::string> parameters);
static void EmitCCValue(VisitResult result, const Stack<std::string>& values,
std::ostream& out);
private:
bool is_cc_debug_;
void EmitSourcePosition(SourcePosition pos,
bool always_emit = false) override;
......
......@@ -334,20 +334,24 @@ void GenerateFieldValueAccessor(const Field& field,
// 0, // Bitfield size (0=not a bitfield)
// 0)); // Bitfield shift
// // The line above is repeated for other struct fields. Omitted here.
// Value<uint16_t> indexed_field_count =
// GetNumberOfAllDescriptorsValue(accessor); // Fetch the array length.
// result.push_back(std::make_unique<ObjectProperty>(
// // Fetch the slice.
// auto indexed_field_slice_descriptors =
// TqDebugFieldSliceDescriptorArrayDescriptors(accessor, address_);
// if (indexed_field_slice_descriptors.validity == d::MemoryAccessResult::kOk) {
// result.push_back(std::make_unique<ObjectProperty>(
// "descriptors", // Field name
// "", // Field type
// "", // Decompressed type
// GetDescriptorsAddress(), // Field address
// indexed_field_count.value, // Number of values
// 24, // Size of value
// address_ - i::kHeapObjectTag +
// std::get<1>(indexed_field_slice_descriptors.value), // Field address
// std::get<2>(indexed_field_slice_descriptors.value), // Number of values
// 12, // Size of value
// std::move(descriptors_struct_field_list), // Struct fields
// GetArrayKind(indexed_field_count.validity))); // Field kind
// GetArrayKind(indexed_field_slice_descriptors.validity))); // Field kind
// }
void GenerateGetPropsChunkForField(const Field& field,
base::Optional<NameAndType> array_length,
std::ostream& get_props_impl) {
std::ostream& get_props_impl,
std::string class_name) {
DebugFieldType debug_field_type(field);
// If the current field is a struct or bitfield struct, create a vector
......@@ -376,27 +380,31 @@ void GenerateGetPropsChunkForField(const Field& field,
// If the field is indexed, emit a fetch of the array length, and change
// count_value and property_kind to be the correct values for an array.
if (array_length) {
const Type* index_type = array_length->type;
std::string index_type_name;
if (index_type == TypeOracle::GetSmiType()) {
index_type_name = "uintptr_t";
count_value =
"i::PlatformSmiTagging::SmiToInt(indexed_field_count.value)";
} else if (!index_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
index_type_name = index_type->GetConstexprGeneratedTypeName();
count_value = "indexed_field_count.value";
} else {
Error("Unsupported index type: ", index_type);
return;
}
get_props_impl << " Value<" << index_type_name
<< "> indexed_field_count = Get"
<< CamelifyString(array_length->name)
<< "Value(accessor);\n";
property_kind = "GetArrayKind(indexed_field_count.validity)";
if (field.index) {
std::string indexed_field_slice =
"indexed_field_slice_" + field.name_and_type.name;
get_props_impl << " auto " << indexed_field_slice << " = "
<< "TqDebugFieldSlice" << class_name
<< CamelifyString(field.name_and_type.name)
<< "(accessor, address_);\n";
std::string validity = indexed_field_slice + ".validity";
std::string value = indexed_field_slice + ".value";
property_kind = "GetArrayKind(" + validity + ")";
get_props_impl << " if (" << validity
<< " == d::MemoryAccessResult::kOk) {\n"
<< " result.push_back(std::make_unique<ObjectProperty>(\""
<< field.name_and_type.name << "\", "
<< debug_field_type.GetTypeString(kAsStoredInHeap) << ", "
<< debug_field_type.GetTypeString(kUncompressed) << ", "
<< "address_ - i::kHeapObjectTag + std::get<1>(" << value
<< "), "
<< "std::get<2>(" << value << ")"
<< ", " << debug_field_type.GetSize() << ", "
<< struct_field_list << ", " << property_kind << "));\n"
<< " }\n";
return;
}
get_props_impl << " result.push_back(std::make_unique<ObjectProperty>(\""
<< field.name_and_type.name << "\", "
<< debug_field_type.GetTypeString(kAsStoredInHeap) << ", "
......@@ -499,21 +507,11 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
for (const Field& field : type.fields()) {
if (field.name_and_type.type == TypeOracle::GetVoidType()) continue;
if (!field.offset.has_value()) {
// Fields with dynamic offset are currently unsupported.
continue;
}
GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
base::Optional<NameAndType> array_length;
if (field.index) {
array_length = ExtractSimpleFieldArraySize(type, *field.index);
if (!array_length) {
// Unsupported complex array length, skipping this field.
continue;
}
if (field.offset.has_value()) {
GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
}
GenerateGetPropsChunkForField(field, array_length, get_props_impl);
GenerateGetPropsChunkForField(field, get_props_impl, name);
}
h_contents << "};\n";
......@@ -556,6 +554,9 @@ void ImplementationVisitor::GenerateClassDebugReaders(
cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
}
cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
cc_contents << "#include \"torque-generated/"
<< "debug-macros"
<< ".h\"\n";
cc_contents << "#include \"include/v8-internal.h\"\n\n";
cc_contents << "namespace i = v8::internal;\n\n";
......
......@@ -294,6 +294,7 @@ class ExternConstant : public Value {
enum class OutputType {
kCSA,
kCC,
kCCDebug,
};
class Callable : public Scope {
......@@ -329,11 +330,23 @@ class Callable : public Scope {
return "TqRuntime" + name;
}
static std::string PrefixNameForCCDebugOutput(const std::string& name) {
// If a Torque macro requires a C++ runtime function to be generated, then
// the generated function begins with this prefix to avoid any naming
// collisions with the generated CSA function for the same macro.
return "TqDebug" + name;
}
// Name to use in runtime C++ code.
virtual std::string CCName() const {
return PrefixNameForCCOutput(ExternalName());
}
// Name to use in debug C++ code.
virtual std::string CCDebugName() const {
return PrefixNameForCCDebugOutput(ExternalName());
}
protected:
Callable(Declarable::Kind kind, std::string external_name,
std::string readable_name, Signature signature,
......@@ -403,6 +416,11 @@ class ExternMacro : public Macro {
"::" + ExternalName();
}
std::string CCDebugName() const override {
return "TorqueDebugMacroShims::" + external_assembler_name() +
"::" + ExternalName();
}
private:
friend class Declarations;
ExternMacro(const std::string& name, std::string external_assembler_name,
......@@ -424,6 +442,12 @@ class TorqueMacro : public Macro {
return PrefixNameForCCOutput(IsExportedToCSA() ? ReadableName()
: ExternalName());
}
std::string CCDebugName() const override {
// Exported functions must have unique and C++-friendly readable names, so
// prefer those wherever possible.
return PrefixNameForCCDebugOutput(IsExportedToCSA() ? ReadableName()
: ExternalName());
}
protected:
TorqueMacro(Declarable::Kind kind, std::string external_name,
......
......@@ -149,6 +149,49 @@ void ImplementationVisitor::EndGeneratedFiles() {
}
}
void ImplementationVisitor::BeginDebugMacrosFile() {
std::ostream& source = debug_macros_cc_;
std::ostream& header = debug_macros_h_;
source << "#include \"torque-generated/debug-macros.h\"\n\n";
source << "#include \"tools/debug_helper/debug-macro-shims.h\"\n";
source << "#include \"include/v8-internal.h\"\n";
source << "\n";
source << "namespace v8 {\n"
<< "namespace internal {\n"
<< "namespace debug_helper_internal {\n"
<< "\n";
const char* kHeaderDefine = "V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_";
header << "#ifndef " << kHeaderDefine << "\n";
header << "#define " << kHeaderDefine << "\n\n";
header << "#include \"src/builtins/torque-csa-header-includes.h\"\n";
header << "#include \"tools/debug_helper/debug-helper-internal.h\"\n";
header << "\n";
header << "namespace v8 {\n"
<< "namespace internal {\n"
<< "namespace debug_helper_internal{\n"
<< "\n";
}
void ImplementationVisitor::EndDebugMacrosFile() {
std::ostream& source = debug_macros_cc_;
std::ostream& header = debug_macros_h_;
source << "} // namespace internal\n"
<< "} // namespace v8\n"
<< "} // namespace debug_helper_internal\n"
<< "\n";
header << "\n} // namespace internal\n"
<< "} // namespace v8\n"
<< "} // namespace debug_helper_internal\n"
<< "\n";
header << "#endif // V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_\n";
}
void ImplementationVisitor::Visit(NamespaceConstant* decl) {
Signature signature{{}, base::nullopt, {{}, false}, 0, decl->type(),
{}, false};
......@@ -322,6 +365,11 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
if (output_type_ == OutputType::kCC) {
csa_ccfile() << "#ifndef V8_INTERNAL_DEFINED_" << macro->CCName() << "\n";
csa_ccfile() << "#define V8_INTERNAL_DEFINED_" << macro->CCName() << "\n";
} else if (output_type_ == OutputType::kCCDebug) {
csa_ccfile() << "#ifndef V8_INTERNAL_DEFINED_" << macro->CCDebugName()
<< "\n";
csa_ccfile() << "#define V8_INTERNAL_DEFINED_" << macro->CCDebugName()
<< "\n";
}
GenerateMacroFunctionDeclaration(csa_ccfile(), macro);
......@@ -331,7 +379,7 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
// For now, generated C++ is only for field offset computations. If we ever
// generate C++ code that can allocate, then it should be handlified.
csa_ccfile() << " DisallowGarbageCollection no_gc;\n";
} else {
} else if (output_type_ == OutputType::kCSA) {
csa_ccfile() << " compiler::CodeAssembler ca_(state_);\n";
csa_ccfile()
<< " compiler::CodeAssembler::SourcePositionScope pos_scope(&ca_);\n";
......@@ -421,6 +469,9 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
if (output_type_ == OutputType::kCC) {
CCGenerator cc_generator{assembler().Result(), csa_ccfile()};
values = cc_generator.EmitGraph(lowered_parameters);
} else if (output_type_ == OutputType::kCCDebug) {
CCGenerator cc_generator{assembler().Result(), csa_ccfile(), true};
values = cc_generator.EmitGraph(lowered_parameters);
} else {
CSAGenerator csa_generator{assembler().Result(), csa_ccfile()};
values = csa_generator.EmitGraph(lowered_parameters);
......@@ -430,7 +481,11 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
if (has_return_value) {
csa_ccfile() << " return ";
if (output_type_ == OutputType::kCC) {
if (output_type_ == OutputType::kCCDebug) {
csa_ccfile() << "{d::MemoryAccessResult::kOk, ";
CCGenerator::EmitCCValue(return_value, *values, csa_ccfile());
csa_ccfile() << "}";
} else if (output_type_ == OutputType::kCC) {
CCGenerator::EmitCCValue(return_value, *values, csa_ccfile());
} else {
CSAGenerator::EmitCSAValue(return_value, *values, csa_ccfile());
......@@ -441,6 +496,9 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
if (output_type_ == OutputType::kCC) {
csa_ccfile() << "#endif // V8_INTERNAL_DEFINED_" << macro->CCName()
<< "\n";
} else if (output_type_ == OutputType::kCCDebug) {
csa_ccfile() << "#endif // V8_INTERNAL_DEFINED_" << macro->CCDebugName()
<< "\n";
}
csa_ccfile() << "\n";
}
......@@ -1677,14 +1735,20 @@ void ImplementationVisitor::GenerateImplementation(const std::string& dir) {
streams.class_definition_inline_headerfile.str());
WriteFile(base_filename + "-tq.cc", streams.class_definition_ccfile.str());
}
WriteFile(dir + "/debug-macros.h", debug_macros_h_.str());
WriteFile(dir + "/debug-macros.cc", debug_macros_cc_.str());
}
void ImplementationVisitor::GenerateMacroFunctionDeclaration(std::ostream& o,
Macro* macro) {
GenerateFunctionDeclaration(
o, "",
output_type_ == OutputType::kCC ? macro->CCName() : macro->ExternalName(),
macro->signature(), macro->parameter_names());
GenerateFunctionDeclaration(o, "",
output_type_ == OutputType::kCC
? macro->CCName()
: output_type_ == OutputType::kCCDebug
? macro->CCDebugName()
: macro->ExternalName(),
macro->signature(), macro->parameter_names());
}
std::vector<std::string> ImplementationVisitor::GenerateFunctionDeclaration(
......@@ -1698,9 +1762,13 @@ std::vector<std::string> ImplementationVisitor::GenerateFunctionDeclaration(
if (signature.return_type->IsVoidOrNever()) {
o << "void";
} else {
o << (output_type_ == OutputType::kCC
? signature.return_type->GetRuntimeType()
: signature.return_type->GetGeneratedTypeName());
if (output_type_ == OutputType::kCCDebug) {
o << "Value<" << signature.return_type->GetDebugType() << ">";
} else {
o << (output_type_ == OutputType::kCC
? signature.return_type->GetRuntimeType()
: signature.return_type->GetGeneratedTypeName());
}
}
o << " " << macro_prefix << name << "(";
......@@ -1708,6 +1776,9 @@ std::vector<std::string> ImplementationVisitor::GenerateFunctionDeclaration(
if (output_type_ == OutputType::kCC) {
first = false;
o << "Isolate* isolate";
} else if (output_type_ == OutputType::kCCDebug) {
first = false;
o << "d::MemoryAccessor accessor";
} else if (pass_code_assembler_state) {
first = false;
o << "compiler::CodeAssemblerState* state_";
......@@ -1721,7 +1792,9 @@ std::vector<std::string> ImplementationVisitor::GenerateFunctionDeclaration(
const std::string& generated_type_name =
output_type_ == OutputType::kCC
? parameter_type->GetRuntimeType()
: parameter_type->GetGeneratedTypeName();
: output_type_ == OutputType::kCCDebug
? parameter_type->GetDebugType()
: parameter_type->GetGeneratedTypeName();
generated_parameter_names.push_back(ExternalParameterName(
i < parameter_names.size() ? parameter_names[i]->value
......@@ -1730,7 +1803,8 @@ std::vector<std::string> ImplementationVisitor::GenerateFunctionDeclaration(
}
for (const LabelDeclaration& label_info : signature.labels) {
if (output_type_ == OutputType::kCC) {
if (output_type_ == OutputType::kCC ||
output_type_ == OutputType::kCCDebug) {
ReportError("Macros that generate runtime code can't have label exits");
}
if (!first) o << ", ";
......@@ -3163,6 +3237,17 @@ void ImplementationVisitor::VisitAllDeclarables() {
// Recover from compile errors here. The error is recorded already.
}
}
// Do the same for macros which generate C++ debug code.
// The set of macros is the same as C++ macros.
output_type_ = OutputType::kCCDebug;
for (size_t i = 0; i < cc_macros.size(); ++i) {
try {
Visit(static_cast<Declarable*>(cc_macros[i].first), cc_macros[i].second);
} catch (TorqueAbortCompilation&) {
// Recover from compile errors here. The error is recorded already.
}
}
output_type_ = OutputType::kCSA;
}
......
......@@ -561,6 +561,8 @@ class ImplementationVisitor {
void BeginGeneratedFiles();
void EndGeneratedFiles();
void BeginDebugMacrosFile();
void EndDebugMacrosFile();
void GenerateImplementation(const std::string& dir);
......@@ -768,19 +770,31 @@ class ImplementationVisitor {
std::ostream& csa_ccfile() {
if (auto* streams = CurrentFileStreams::Get()) {
return output_type_ == OutputType::kCSA
? streams->csa_ccfile
: streams
->class_definition_inline_headerfile_macro_definitions;
switch (output_type_) {
case OutputType::kCSA:
return streams->csa_ccfile;
case OutputType::kCC:
return streams->class_definition_inline_headerfile_macro_definitions;
case OutputType::kCCDebug:
return debug_macros_cc_;
default:
UNREACHABLE();
}
}
return null_stream_;
}
std::ostream& csa_headerfile() {
if (auto* streams = CurrentFileStreams::Get()) {
return output_type_ == OutputType::kCSA
? streams->csa_headerfile
: streams
->class_definition_inline_headerfile_macro_declarations;
switch (output_type_) {
case OutputType::kCSA:
return streams->csa_headerfile;
case OutputType::kCC:
return streams->class_definition_inline_headerfile_macro_declarations;
case OutputType::kCCDebug:
return debug_macros_h_;
default:
UNREACHABLE();
}
}
return null_stream_;
}
......@@ -832,6 +846,11 @@ class ImplementationVisitor {
std::unordered_map<const Expression*, const Identifier*>
bitfield_expressions_;
// The contents of the debug macros output files. These contain all Torque
// macros that have been generated using the C++ backend with debug purpose.
std::stringstream debug_macros_cc_;
std::stringstream debug_macros_h_;
OutputType output_type_ = OutputType::kCSA;
};
......
......@@ -76,6 +76,7 @@ void CompileCurrentAst(TorqueCompilerOptions options) {
implementation_visitor.GenerateInstanceTypes(output_directory);
implementation_visitor.BeginGeneratedFiles();
implementation_visitor.BeginDebugMacrosFile();
implementation_visitor.VisitAllDeclarables();
......@@ -95,6 +96,7 @@ void CompileCurrentAst(TorqueCompilerOptions options) {
implementation_visitor.GenerateCSATypes(output_directory);
implementation_visitor.EndGeneratedFiles();
implementation_visitor.EndDebugMacrosFile();
implementation_visitor.GenerateImplementation(output_directory);
if (GlobalContext::collect_language_server_data()) {
......
......@@ -1302,6 +1302,26 @@ std::string Type::GetRuntimeType() const {
return ConstexprVersion()->GetGeneratedTypeName();
}
std::string Type::GetDebugType() const {
if (IsSubtypeOf(TypeOracle::GetSmiType())) return "uintptr_t";
if (IsSubtypeOf(TypeOracle::GetTaggedType())) {
return "uintptr_t";
}
if (base::Optional<const StructType*> struct_type = StructSupertype()) {
std::stringstream result;
result << "std::tuple<";
bool first = true;
for (const Type* field_type : LowerType(*struct_type)) {
if (!first) result << ", ";
first = false;
result << field_type->GetDebugType();
}
result << ">";
return result.str();
}
return ConstexprVersion()->GetGeneratedTypeName();
}
} // namespace torque
} // namespace internal
} // namespace v8
......@@ -142,6 +142,7 @@ class V8_EXPORT_PRIVATE Type : public TypeBase {
base::Optional<const AggregateType*> AggregateSupertype() const;
virtual std::vector<TypeChecker> GetTypeCheckers() const { return {}; }
virtual std::string GetRuntimeType() const;
virtual std::string GetDebugType() const;
static const Type* CommonSupertype(const Type* a, const Type* b);
void AddAlias(std::string alias) const { aliases_.insert(std::move(alias)); }
size_t id() const { return id_; }
......@@ -398,6 +399,7 @@ class V8_EXPORT_PRIVATE UnionType final : public Type {
std::string GetRuntimeType() const override {
return parent()->GetRuntimeType();
}
std::string GetDebugType() const override { return parent()->GetDebugType(); }
friend size_t hash_value(const UnionType& p) {
size_t result = 0;
......
......@@ -450,5 +450,46 @@ THREADED_TEST(GetFrameStack) {
.ToLocalChecked();
}
TEST(SmallOrderedHashSetGetObjectProperties) {
LocalContext context;
Isolate* isolate = reinterpret_cast<Isolate*>((*context)->GetIsolate());
Factory* factory = isolate->factory();
HandleScope scope(isolate);
Handle<SmallOrderedHashSet> set = factory->NewSmallOrderedHashSet();
const size_t number_of_buckets = 2;
CHECK_EQ(number_of_buckets, set->NumberOfBuckets());
CHECK_EQ(0, set->NumberOfElements());
// Verify with the definition of SmallOrderedHashSet in
// src\objects\ordered-hash-table.tq.
d::HeapAddresses heap_addresses{0, 0, 0, 0};
d::ObjectPropertiesResultPtr props =
d::GetObjectProperties(set->ptr(), &ReadMemory, heap_addresses);
CHECK_EQ(props->type_check_result, d::TypeCheckResult::kUsedMap);
CHECK_EQ(props->type, std::string("v8::internal::SmallOrderedHashSet"));
CHECK_EQ(props->num_properties, 8);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CheckProp(*props->properties[1], "uint8_t", "number_of_elements");
CheckProp(*props->properties[2], "uint8_t", "number_of_deleted_elements");
CheckProp(*props->properties[3], "uint8_t", "number_of_buckets");
#if TAGGED_SIZE_8_BYTES
CheckProp(*props->properties[4], "uint8_t", "padding",
d::PropertyKind::kArrayOfKnownSize, 5);
#else
CheckProp(*props->properties[4], "uint8_t", "padding",
d::PropertyKind::kArrayOfKnownSize, 1);
#endif
CheckProp(*props->properties[5], "v8::internal::Object", "data_table",
d::PropertyKind::kArrayOfKnownSize,
number_of_buckets * OrderedHashMap::kLoadFactor);
CheckProp(*props->properties[6], "uint8_t", "hash_table",
d::PropertyKind::kArrayOfKnownSize, number_of_buckets);
CheckProp(*props->properties[7], "uint8_t", "chain_table",
d::PropertyKind::kArrayOfKnownSize,
number_of_buckets * OrderedHashMap::kLoadFactor);
}
} // namespace internal
} // namespace v8
......@@ -77,6 +77,8 @@ v8_component("v8_debug_helper") {
sources = [
"$target_gen_dir/../../torque-generated/class-debug-readers.cc",
"$target_gen_dir/../../torque-generated/class-debug-readers.h",
"$target_gen_dir/../../torque-generated/debug-macros.cc",
"$target_gen_dir/../../torque-generated/debug-macros.h",
"$target_gen_dir/../../torque-generated/instance-types.h",
"$target_gen_dir/heap-constants-gen.cc",
"compiler-types.cc",
......
// Copyright 2020 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.
// This file contains implementations of a few macros that are defined
// as external in Torque, so that generated debug code can work.
#ifndef V8_TORQUE_DEBUG_MACRO_SHIMS_H_
#define V8_TORQUE_DEBUG_MACRO_SHIMS_H_
#include "src/objects/smi.h"
#include "tools/debug_helper/debug-helper-internal.h"
// For Object::ReadField<T>.
#define READ_FIELD_OR_FAIL(Type, destination, accessor, object, offset) \
do { \
Type value{}; \
d::MemoryAccessResult validity = \
accessor(object - kHeapObjectTag + offset, \
reinterpret_cast<Type*>(&value), sizeof(value)); \
if (validity != d::MemoryAccessResult::kOk) return {validity, {}}; \
destination = value; \
} while (false)
// For TaggedField<T>::load.
#define READ_TAGGED_FIELD_OR_FAIL(destination, accessor, object, offset) \
do { \
Tagged_t value{}; \
d::MemoryAccessResult validity = \
accessor(object - kHeapObjectTag + offset, \
reinterpret_cast<uint8_t*>(&value), sizeof(value)); \
if (validity != d::MemoryAccessResult::kOk) return {validity, {}}; \
destination = EnsureDecompressed(value, object); \
} while (false)
// Process Value struct.
#define ASSIGN_OR_RETURN(dest, val) \
do { \
if ((val).validity != d::MemoryAccessResult::kOk) \
return {(val).validity, {}}; \
dest = (val).value; \
} while (false)
namespace v8 {
namespace internal {
namespace debug_helper_internal {
namespace TorqueDebugMacroShims {
namespace CodeStubAssembler {
inline Value<intptr_t> ChangeInt32ToIntPtr(d::MemoryAccessor accessor,
int32_t i) {
return {d::MemoryAccessResult::kOk, i};
}
inline Value<uintptr_t> ChangeUint32ToWord(d::MemoryAccessor accessor,
uint32_t u) {
return {d::MemoryAccessResult::kOk, u};
}
inline Value<intptr_t> IntPtrAdd(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a + b};
}
inline Value<intptr_t> IntPtrMul(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a * b};
}
inline Value<intptr_t> Signed(d::MemoryAccessor accessor, uintptr_t u) {
return {d::MemoryAccessResult::kOk, static_cast<intptr_t>(u)};
}
inline Value<int32_t> SmiUntag(d::MemoryAccessor accessor, uintptr_t s_t) {
Smi s(s_t);
return {d::MemoryAccessResult::kOk, s.value()};
}
} // namespace CodeStubAssembler
} // namespace TorqueDebugMacroShims
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_DEBUG_MACRO_SHIMS_H_
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