blob: 2b53383f61acf78ed88f1370b9e69e304c4c57d6 [file] [log] [blame]
// 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 "src/torque/implementation-visitor.h"
namespace v8 {
namespace internal {
namespace torque {
namespace {
// Contains all necessary state for a single class type during the process of
// assigning instance types, and provides a convenient way to access the list of
// types that inherit from this one.
struct InstanceTypeTree {
explicit InstanceTypeTree(const ClassType* type)
: type(type),
start(INT_MAX),
end(INT_MIN),
value(-1),
num_values(0),
num_own_values(0) {}
const ClassType* type;
std::vector<std::unique_ptr<InstanceTypeTree>> children;
int start; // Start of range for this and subclasses, or INT_MAX.
int end; // End of range for this and subclasses, or INT_MIN.
int value; // Assigned value for this class itself, or -1 when unassigned.
int num_values; // Number of values assigned for this and subclasses.
int num_own_values; // How many values this needs (not including subclasses).
};
// Assembles all class types into a tree, but doesn't yet attempt to assign
// instance types for them.
std::unique_ptr<InstanceTypeTree> BuildInstanceTypeTree() {
// First, build InstanceTypeTree instances for every class but don't try to
// attach them to their subclasses yet.
std::unordered_map<const ClassType*, InstanceTypeTree*> map_by_type;
std::vector<std::unique_ptr<InstanceTypeTree>> unparented_types;
for (auto& p : GlobalContext::AllDeclarables()) {
if (const TypeAlias* alias = TypeAlias::DynamicCast(p.get())) {
const Type* type = alias->type();
const ClassType* class_type = ClassType::DynamicCast(type);
if (class_type == nullptr) {
continue;
}
auto& map_slot = map_by_type[class_type];
if (map_slot != nullptr) {
continue; // We already encountered this type.
}
std::unique_ptr<InstanceTypeTree> type_tree =
std::make_unique<InstanceTypeTree>(class_type);
map_slot = type_tree.get();
unparented_types.push_back(std::move(type_tree));
}
}
// Second, assemble them all into a tree following the inheritance hierarchy.
std::unique_ptr<InstanceTypeTree> root;
for (auto& type_tree : unparented_types) {
const ClassType* parent = type_tree->type->GetSuperClass();
if (parent == nullptr) {
if (root != nullptr)
Error("Expected only one root class type. Found: ", root->type->name(),
" and ", type_tree->type->name())
.Position(type_tree->type->GetPosition());
root = std::move(type_tree);
} else {
map_by_type[parent]->children.push_back(std::move(type_tree));
}
}
return root;
}
// Propagates constraints about instance types from children to their parents.
void PropagateInstanceTypeConstraints(InstanceTypeTree* root) {
for (auto& child : root->children) {
PropagateInstanceTypeConstraints(child.get());
if (child->start < root->start) root->start = child->start;
if (child->end > root->end) root->end = child->end;
root->num_values += child->num_values;
}
const InstanceTypeConstraints& constraints =
root->type->GetInstanceTypeConstraints();
if (!root->type->IsAbstract() && !root->type->HasSameInstanceTypeAsParent()) {
root->num_own_values = 1;
}
root->num_values += root->num_own_values;
if (constraints.num_flags_bits != -1) {
// Children won't get any types assigned; must be done manually in C++.
root->children.clear();
root->num_values = 1 << constraints.num_flags_bits;
root->num_own_values = root->num_values;
root->start = 0;
root->end = root->num_values - 1;
}
if (constraints.value != -1) {
if (root->num_own_values != 1) {
Error("Instance type value requested for abstract class ",
root->type->name())
.Position(root->type->GetPosition());
}
root->value = constraints.value;
if (constraints.value < root->start) root->start = constraints.value;
if (constraints.value > root->end) root->end = constraints.value;
}
}
// Assigns values for the type itself, not including any children. Returns the
// next available value.
int SelectOwnValues(InstanceTypeTree* root, int start_value) {
if (root->value == -1) {
root->value = start_value;
} else if (root->value < start_value) {
Error("Failed to assign instance type ", root->value, " to ",
root->type->name())
.Position(root->type->GetPosition());
}
return root->value + root->num_own_values;
}
// Sorting function for types that don't have specific values they must include.
// Prioritizes bigger type ranges (those with more subtypes) first, and
// then sorts alphabetically within each size category.
struct CompareUnconstrainedTypes {
constexpr bool operator()(const InstanceTypeTree* a,
const InstanceTypeTree* b) const {
return (a->num_values > b->num_values)
? true
: (a->num_values < b->num_values)
? false
: std::less<std::string>()(a->type->name(),
b->type->name());
}
};
// Assigns concrete values for every instance type range, and sorts the children
// at each layer of the tree into increasing order. Appends the newly-assigned
// tree to the destination vector. Returns the first unassigned value after
// those that have been used.
int SolveInstanceTypeConstraints(
std::unique_ptr<InstanceTypeTree> root, int start_value,
std::vector<std::unique_ptr<InstanceTypeTree>>* destination) {
if (root->start < start_value) {
Error("Failed to assign instance type ", root->start, " to ",
root->type->name())
.Position(root->type->GetPosition());
}
// First, separate the children into four groups:
// - The one child that must go first, if it exists;
// - Children with specific value requirements ("constrained");
// - Children without specific value requirements ("unconstrained");
// - The one child that must go last, if it exists.
std::unique_ptr<InstanceTypeTree> lowest_child;
std::unique_ptr<InstanceTypeTree> highest_child;
std::multimap<int, std::unique_ptr<InstanceTypeTree>>
constrained_children_by_start;
// Using std::map because you can't std::move out of a std::set until C++17.
std::map<InstanceTypeTree*, std::unique_ptr<InstanceTypeTree>,
CompareUnconstrainedTypes>
unconstrained_children_by_size;
for (auto& child : root->children) {
if (child->type->IsHighestInstanceTypeWithinParent()) {
if (highest_child) {
Error("Two classes requested to be the highest instance type: ",
highest_child->type->name(), " and ", child->type->name(),
" within range for parent class ", root->type->name())
.Position(child->type->GetPosition());
}
if (child->type->IsLowestInstanceTypeWithinParent()) {
Error(
"Class requested to be both highest and lowest instance type "
"within its parent range: ",
child->type->name())
.Position(child->type->GetPosition());
}
highest_child = std::move(child);
} else if (child->type->IsLowestInstanceTypeWithinParent()) {
if (lowest_child) {
Error("Two classes requested to be the lowest instance type: ",
lowest_child->type->name(), " and ", child->type->name(),
" within range for parent class ", root->type->name())
.Position(child->type->GetPosition());
}
lowest_child = std::move(child);
} else if (child->start > child->end) {
unconstrained_children_by_size.insert(
std::make_pair(child.get(), std::move(child)));
} else {
constrained_children_by_start.insert(
std::make_pair(child->start, std::move(child)));
}
}
root->children.clear();
bool own_type_pending = root->num_own_values > 0;
// Second, iterate and place the children in ascending order.
if (lowest_child != nullptr) {
start_value = SolveInstanceTypeConstraints(std::move(lowest_child),
start_value, &root->children);
}
for (auto& constrained_child_pair : constrained_children_by_start) {
// Select the next constrained child type in ascending order.
std::unique_ptr<InstanceTypeTree> constrained_child =
std::move(constrained_child_pair.second);
// Try to place the root type before the constrained child type if it fits.
if (own_type_pending) {
if ((root->value != -1 && root->value < constrained_child->start) ||
(root->value == -1 &&
start_value + root->num_own_values <= constrained_child->start)) {
start_value = SelectOwnValues(root.get(), start_value);
own_type_pending = false;
}
}
// Try to find any unconstrained children that fit before the constrained
// one. This simple greedy algorithm just puts the biggest unconstrained
// children in first, which might not fill the space as efficiently as
// possible but is good enough for our needs.
for (auto it = unconstrained_children_by_size.begin();
it != unconstrained_children_by_size.end();) {
if (it->second->num_values + start_value <= constrained_child->start) {
start_value = SolveInstanceTypeConstraints(
std::move(it->second), start_value, &root->children);
it = unconstrained_children_by_size.erase(it);
} else {
++it;
}
}
// Place the constrained child type.
start_value = SolveInstanceTypeConstraints(std::move(constrained_child),
start_value, &root->children);
}
if (own_type_pending) {
start_value = SelectOwnValues(root.get(), start_value);
own_type_pending = false;
}
for (auto& child_pair : unconstrained_children_by_size) {
start_value = SolveInstanceTypeConstraints(std::move(child_pair.second),
start_value, &root->children);
}
if (highest_child != nullptr) {
start_value = SolveInstanceTypeConstraints(std::move(highest_child),
start_value, &root->children);
}
// Finally, set the range for this class to include all placed subclasses.
root->end = start_value - 1;
root->start =
root->children.empty() ? start_value : root->children.front()->start;
if (root->value != -1 && root->value < root->start) {
root->start = root->value;
}
root->num_values = root->end - root->start + 1;
root->type->InitializeInstanceTypes(
root->value == -1 ? base::Optional<int>{} : root->value,
std::make_pair(root->start, root->end));
if (root->num_values > 0) {
destination->push_back(std::move(root));
}
return start_value;
}
std::unique_ptr<InstanceTypeTree> SolveInstanceTypeConstraints(
std::unique_ptr<InstanceTypeTree> root) {
std::vector<std::unique_ptr<InstanceTypeTree>> destination;
SolveInstanceTypeConstraints(std::move(root), 0, &destination);
return destination.empty() ? nullptr : std::move(destination.front());
}
std::unique_ptr<InstanceTypeTree> AssignInstanceTypes() {
std::unique_ptr<InstanceTypeTree> root = BuildInstanceTypeTree();
if (root != nullptr) {
PropagateInstanceTypeConstraints(root.get());
root = SolveInstanceTypeConstraints(std::move(root));
}
return root;
}
// Prints items in macro lists for the given type and its descendants.
// - definitions: This list is pairs of instance type name and assigned value,
// such as V(ODDBALL_TYPE, 67). It includes FIRST_* and LAST_* items for each
// type that has more than one associated InstanceType. Items within those
// ranges are indented for readability.
// - values: This list is just instance type names, like V(ODDBALL_TYPE). It
// does not include any FIRST_* and LAST_* range markers.
// - fully_defined_single_instance_types: This list is pairs of class name and
// instance type, for classes which have defined layouts and a single
// corresponding instance type.
// - fully_defined_multiple_instance_types: This list is pairs of class name and
// instance type, for classes which have defined layouts and subclasses.
// - only_declared_single_instance_types: This list is pairs of class name and
// instance type, for classes which have a single corresponding instance type
// and do not have layout definitions in Torque.
// - fully_defined_range_instance_types: This list is triples of class name,
// first instance type, and last instance type, for classes which have defined
// layouts and multiple corresponding instance types.
// - only_declared_range_instance_types: This list is triples of class name,
// first instance type, and last instance type, for classes which have
// multiple corresponding instance types and do not have layout definitions in
// Torque.
void PrintInstanceTypes(InstanceTypeTree* root, std::ostream& definitions,
std::ostream& values,
std::ostream& fully_defined_single_instance_types,
std::ostream& fully_defined_multiple_instance_types,
std::ostream& only_declared_single_instance_types,
std::ostream& fully_defined_range_instance_types,
std::ostream& only_declared_range_instance_types,
const std::string& indent) {
std::string type_name =
CapifyStringWithUnderscores(root->type->name()) + "_TYPE";
std::string inner_indent = indent;
if (root->num_values > 1) {
definitions << indent << "V(FIRST_" << type_name << ", " << root->start
<< ") \\\n";
inner_indent += " ";
}
if (root->num_own_values == 1) {
definitions << inner_indent << "V(" << type_name << ", " << root->value
<< ") \\\n";
values << " V(" << type_name << ") \\\n";
if (root->num_values == 1) {
std::ostream& single_instance_types =
root->type->HasUndefinedLayout()
? only_declared_single_instance_types
: fully_defined_single_instance_types;
single_instance_types << " V(" << root->type->name() << ", " << type_name
<< ") \\\n";
}
}
for (auto& child : root->children) {
PrintInstanceTypes(
child.get(), definitions, values, fully_defined_single_instance_types,
fully_defined_multiple_instance_types,
only_declared_single_instance_types, fully_defined_range_instance_types,
only_declared_range_instance_types, inner_indent);
}
if (root->num_values > 1) {
// We can't emit LAST_STRING_TYPE because it's not a valid flags
// combination. So if the class type has multiple own values, which only
// happens when using ANNOTATION_RESERVE_BITS_IN_INSTANCE_TYPE, then omit
// the end marker.
if (root->num_own_values <= 1) {
definitions << indent << "V(LAST_" << type_name << ", " << root->end
<< ") \\\n";
}
// Only output the instance type range for things other than the root type.
if (root->type->GetSuperClass() != nullptr) {
std::ostream& range_instance_types =
root->type->HasUndefinedLayout() ? only_declared_range_instance_types
: fully_defined_range_instance_types;
range_instance_types << " V(" << root->type->name() << ", FIRST_"
<< type_name << ", LAST_" << type_name << ") \\\n";
if (!root->type->IsExtern() && !root->type->IsAbstract() &&
!root->type->HasUndefinedLayout()) {
fully_defined_multiple_instance_types << " V(" << root->type->name()
<< ", " << type_name << ") \\\n";
}
}
}
}
} // namespace
void ImplementationVisitor::GenerateInstanceTypes(
const std::string& output_directory) {
std::stringstream header;
std::string file_name = "instance-types-tq.h";
{
IncludeGuardScope guard(header, file_name);
header << "// Instance types for all classes except for those that use "
"InstanceType as flags.\n";
header << "#define TORQUE_ASSIGNED_INSTANCE_TYPES(V) \\\n";
std::unique_ptr<InstanceTypeTree> instance_types = AssignInstanceTypes();
std::stringstream values_list;
std::stringstream fully_defined_single_instance_types;
std::stringstream fully_defined_multiple_instance_types;
std::stringstream only_declared_single_instance_types;
std::stringstream fully_defined_range_instance_types;
std::stringstream only_declared_range_instance_types;
if (instance_types != nullptr) {
PrintInstanceTypes(instance_types.get(), header, values_list,
fully_defined_single_instance_types,
fully_defined_multiple_instance_types,
only_declared_single_instance_types,
fully_defined_range_instance_types,
only_declared_range_instance_types, " ");
}
header << "\n";
header << "// Instance types for all classes except for those that use\n";
header << "// InstanceType as flags.\n";
header << "#define TORQUE_ASSIGNED_INSTANCE_TYPE_LIST(V) \\\n";
header << values_list.str();
header << "\n";
header << "// Pairs of (ClassName, INSTANCE_TYPE) for classes that have\n";
header << "// full Torque definitions.\n";
header << "#define TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(V) \\\n";
header << fully_defined_single_instance_types.str();
header << "\n";
header << "// Pairs of (ClassName, INSTANCE_TYPE) for classes that have\n";
header << "// full Torque definitions and subclasses.\n";
header << "#define TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(V) \\\n";
header << fully_defined_multiple_instance_types.str();
header << "\n";
header << "// Pairs of (ClassName, INSTANCE_TYPE) for classes that are\n";
header << "// declared but not defined in Torque. These classes may\n";
header << "// correspond with actual C++ classes, but they are not\n";
header << "// guaranteed to.\n";
header << "#define TORQUE_INSTANCE_CHECKERS_SINGLE_ONLY_DECLARED(V) \\\n";
header << only_declared_single_instance_types.str();
header << "\n";
header << "// Triples of (ClassName, FIRST_TYPE, LAST_TYPE) for classes\n";
header << "// that have full Torque definitions.\n";
header << "#define TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(V) \\\n";
header << fully_defined_range_instance_types.str();
header << "\n";
header << "// Triples of (ClassName, FIRST_TYPE, LAST_TYPE) for classes\n";
header << "// that are declared but not defined in Torque. These classes\n";
header << "// may correspond with actual C++ classes, but they are not\n";
header << "// guaranteed to.\n";
header << "#define TORQUE_INSTANCE_CHECKERS_RANGE_ONLY_DECLARED(V) \\\n";
header << only_declared_range_instance_types.str();
header << "\n";
std::stringstream torque_defined_class_list;
std::stringstream torque_defined_varsize_instance_type_list;
std::stringstream torque_defined_fixed_instance_type_list;
std::stringstream torque_defined_map_csa_list;
std::stringstream torque_defined_map_root_list;
for (const ClassType* type : TypeOracle::GetClasses()) {
std::string upper_case_name = type->name();
std::string lower_case_name = SnakeifyString(type->name());
std::string instance_type_name =
CapifyStringWithUnderscores(type->name()) + "_TYPE";
if (type->IsExtern()) continue;
torque_defined_class_list << " V(" << upper_case_name << ") \\\n";
if (type->IsAbstract()) continue;
torque_defined_map_csa_list << " V(_, " << upper_case_name << "Map, "
<< lower_case_name << "_map, "
<< upper_case_name << ") \\\n";
torque_defined_map_root_list << " V(Map, " << lower_case_name << "_map, "
<< upper_case_name << "Map) \\\n";
std::stringstream& list = type->HasStaticSize()
? torque_defined_fixed_instance_type_list
: torque_defined_varsize_instance_type_list;
list << " V(" << instance_type_name << ", " << upper_case_name << ", "
<< lower_case_name << ") \\\n";
}
header << "// Fully Torque-defined classes (both internal and exported).\n";
header << "#define TORQUE_DEFINED_CLASS_LIST(V) \\\n";
header << torque_defined_class_list.str();
header << "\n";
header << "#define TORQUE_DEFINED_VARSIZE_INSTANCE_TYPE_LIST(V) \\\n";
header << torque_defined_varsize_instance_type_list.str();
header << "\n";
header << "#define TORQUE_DEFINED_FIXED_INSTANCE_TYPE_LIST(V) \\\n";
header << torque_defined_fixed_instance_type_list.str();
header << "\n";
header << "#define TORQUE_DEFINED_INSTANCE_TYPE_LIST(V) \\\n";
header << " TORQUE_DEFINED_VARSIZE_INSTANCE_TYPE_LIST(V) \\\n";
header << " TORQUE_DEFINED_FIXED_INSTANCE_TYPE_LIST(V) \\\n";
header << "\n";
header << "#define TORQUE_DEFINED_MAP_CSA_LIST_GENERATOR(V, _) \\\n";
header << torque_defined_map_csa_list.str();
header << "\n";
header << "#define TORQUE_DEFINED_MAP_ROOT_LIST(V) \\\n";
header << torque_defined_map_root_list.str();
header << "\n";
}
std::string output_header_path = output_directory + "/" + file_name;
WriteFile(output_header_path, header.str());
GlobalContext::SetInstanceTypesInitialized();
}
} // namespace torque
} // namespace internal
} // namespace v8