Commit a9a1a3bb authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] add references to HeapObject fields.

This adds references to HeapObject fields to Torque.
The syntax is based on Rust (which is essentially C pointer syntax).

The type &T is a reference to T (which must be a scalar type for now).
We can create references from field access expressions, using the
addressof(&) operator:
  &obj.fieldname
To read or assign a reference, we use the dereference(*) operator:
  *someref = *otherref

This CL also uses references internally normal class field accesses,
but only if there is no overload for field accessor functions.
This allows to have overloaded field accessors for a subtype like
FastJSArray. However, there is a change in behavior in that an
operator ".fieldname" will stop reference creation and will therefore
also stop write access to a class field of the same name. That's why
this CL had to add a write overload ".length=" for FastJSArray.

References desugar to a pair of a tagged HeapObject pointer and an
untagged offset into this HeapObject. On the CSA-side, they are
represented by the C++ struct

struct TorqueReference {
  TNode<HeapObject> object;
  TNode<IntPtrT> offset;
};

Bug: v8:7793
Change-Id: Ica6468d47847bd68fb6b85f731cf8fbe142fa401
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1557151
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60780}
parent de6a07dc
......@@ -39,12 +39,12 @@ type int31 extends int32
type uint31 extends uint32
generates 'TNode<Uint32T>' constexpr 'uint31_t';
type int16 extends int31
generates 'TNode<Int32T>' constexpr 'int16_t';
generates 'TNode<Int16T>' constexpr 'int16_t';
type uint16 extends uint31
generates 'TNode<Uint32T>' constexpr 'uint16_t';
type int8 extends int16 generates 'TNode<Int32T>' constexpr 'int8_t';
generates 'TNode<Uint16T>' constexpr 'uint16_t';
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
type uint8 extends uint16
generates 'TNode<Uint32T>' constexpr 'uint8_t';
generates 'TNode<Uint8T>' constexpr 'uint8_t';
type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
type uintptr generates 'TNode<UintPtrT>' constexpr 'uintptr_t';
......@@ -1760,6 +1760,11 @@ extern operator '.elements_kind' macro LoadElementsKind(JSTypedArray):
ElementsKind;
extern operator '.length' macro LoadFastJSArrayLength(FastJSArray): Smi;
operator '.length=' macro StoreFastJSArrayLength(
array: FastJSArray, length: Smi) {
const array: JSArray = array;
array.length = length;
}
extern operator '.objects[]' macro LoadFixedArrayElement(
FixedArray, intptr): Object;
......
......@@ -851,6 +851,54 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
LoadObjectField(object, offset, MachineType::AnyTagged()));
}
// Reference is the CSA-equivalent of a Torque reference value,
// representing an inner pointer into a HeapObject.
struct Reference {
TNode<HeapObject> object;
TNode<IntPtrT> offset;
std::tuple<TNode<HeapObject>, TNode<IntPtrT>> Flatten() const {
return std::make_tuple(object, offset);
}
};
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<Object>>::value,
int>::type = 0>
TNode<T> LoadReference(Reference reference) {
return CAST(LoadObjectField(reference.object, reference.offset,
MachineTypeOf<T>::value));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value,
int>::type = 0>
TNode<T> LoadReference(Reference reference) {
return UncheckedCast<T>(LoadObjectField(reference.object, reference.offset,
MachineTypeOf<T>::value));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<Object>>::value,
int>::type = 0>
void StoreReference(Reference reference, TNode<T> value) {
int const_offset;
if (std::is_same<T, Smi>::value) {
StoreObjectFieldNoWriteBarrier(reference.object, reference.offset, value);
} else if (std::is_same<T, Map>::value &&
ToInt32Constant(reference.offset, const_offset) &&
const_offset == HeapObject::kMapOffset) {
StoreMap(reference.object, value);
} else {
StoreObjectField(reference.object, reference.offset, value);
}
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value,
int>::type = 0>
void StoreReference(Reference reference, TNode<T> value) {
StoreObjectFieldNoWriteBarrier<T>(reference.object, reference.offset,
value);
}
// Tag a smi and store it.
void StoreAndTagSmi(Node* base, int offset, Node* value);
......@@ -2882,7 +2930,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Update the type feedback vector.
void UpdateFeedback(Node* feedback, Node* feedback_vector, Node* slot_id);
// Report that there was a feedback update, performing any tasks that should
// be done after a feedback update.
void ReportFeedbackUpdate(SloppyTNode<FeedbackVector> feedback_vector,
......
......@@ -107,6 +107,18 @@ struct Int32T : Word32T {
struct Uint32T : Word32T {
static constexpr MachineType kMachineType = MachineType::Uint32();
};
struct Int16T : Int32T {
static constexpr MachineType kMachineType = MachineType::Int16();
};
struct Uint16T : Uint32T {
static constexpr MachineType kMachineType = MachineType::Uint16();
};
struct Int8T : Int16T {
static constexpr MachineType kMachineType = MachineType::Int8();
};
struct Uint8T : Uint16T {
static constexpr MachineType kMachineType = MachineType::Uint8();
};
struct Word64T : IntegralT {
static const MachineRepresentation kMachineRepresentation =
......
......@@ -20,8 +20,6 @@ namespace torque {
#define AST_EXPRESSION_NODE_KIND_LIST(V) \
V(CallExpression) \
V(CallMethodExpression) \
V(LoadObjectFieldExpression) \
V(StoreObjectFieldExpression) \
V(IntrinsicCallExpression) \
V(StructExpression) \
V(LogicalOrExpression) \
......@@ -33,6 +31,7 @@ namespace torque {
V(NumberLiteralExpression) \
V(FieldAccessExpression) \
V(ElementAccessExpression) \
V(DereferenceExpression) \
V(AssignmentExpression) \
V(IncrementDecrementExpression) \
V(NewExpression) \
......@@ -43,7 +42,8 @@ namespace torque {
#define AST_TYPE_EXPRESSION_NODE_KIND_LIST(V) \
V(BasicTypeExpression) \
V(FunctionTypeExpression) \
V(UnionTypeExpression)
V(UnionTypeExpression) \
V(ReferenceTypeExpression)
#define AST_STATEMENT_NODE_KIND_LIST(V) \
V(BlockStatement) \
......@@ -222,31 +222,6 @@ struct IdentifierExpression : LocationExpression {
std::vector<TypeExpression*> generic_arguments;
};
struct LoadObjectFieldExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(LoadObjectFieldExpression)
LoadObjectFieldExpression(SourcePosition pos, Expression* base,
std::string field_name)
: Expression(kKind, pos),
base(std::move(base)),
field_name(std::move(field_name)) {}
Expression* base;
std::string field_name;
};
struct StoreObjectFieldExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(StoreObjectFieldExpression)
StoreObjectFieldExpression(SourcePosition pos, Expression* base,
std::string field_name, Expression* value)
: Expression(kKind, pos),
base(std::move(base)),
field_name(std::move(field_name)),
value(std::move(value)) {}
Expression* base;
std::string field_name;
Expression* value;
size_t offset;
};
struct IntrinsicCallExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(IntrinsicCallExpression)
IntrinsicCallExpression(SourcePosition pos, std::string name,
......@@ -376,15 +351,25 @@ struct FieldAccessExpression : LocationExpression {
Identifier* field;
};
struct DereferenceExpression : LocationExpression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(DereferenceExpression)
DereferenceExpression(SourcePosition pos, Expression* reference)
: LocationExpression(kKind, pos), reference(reference) {}
Expression* reference;
};
struct AssignmentExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(AssignmentExpression)
AssignmentExpression(SourcePosition pos, LocationExpression* location,
AssignmentExpression(SourcePosition pos, Expression* location,
Expression* value)
: AssignmentExpression(pos, location, base::nullopt, value) {}
AssignmentExpression(SourcePosition pos, Expression* location,
base::Optional<std::string> op, Expression* value)
: Expression(kKind, pos),
location(location),
op(std::move(op)),
value(value) {}
LocationExpression* location;
Expression* location;
base::Optional<std::string> op;
Expression* value;
};
......@@ -393,10 +378,10 @@ enum class IncrementDecrementOperator { kIncrement, kDecrement };
struct IncrementDecrementExpression : Expression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(IncrementDecrementExpression)
IncrementDecrementExpression(SourcePosition pos, LocationExpression* location,
IncrementDecrementExpression(SourcePosition pos, Expression* location,
IncrementDecrementOperator op, bool postfix)
: Expression(kKind, pos), location(location), op(op), postfix(postfix) {}
LocationExpression* location;
Expression* location;
IncrementDecrementOperator op;
bool postfix;
};
......@@ -480,6 +465,13 @@ struct UnionTypeExpression : TypeExpression {
TypeExpression* b;
};
struct ReferenceTypeExpression : TypeExpression {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ReferenceTypeExpression)
ReferenceTypeExpression(SourcePosition pos, TypeExpression* referenced_type)
: TypeExpression(kKind, pos), referenced_type(referenced_type) {}
TypeExpression* referenced_type;
};
struct ExpressionStatement : Statement {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ExpressionStatement)
ExpressionStatement(SourcePosition pos, Expression* expression)
......
......@@ -307,7 +307,9 @@ void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
std::string catch_name =
PreCallableExceptionPreparation(instruction.catch_block);
out_ << " ";
if (return_type->IsStructType()) {
bool needs_flattening =
return_type->IsStructType() || return_type->IsReferenceType();
if (needs_flattening) {
out_ << "std::tie(";
PrintCommaSeparatedList(out_, results);
out_ << ") = ";
......@@ -315,12 +317,14 @@ void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
if (results.size() == 1) {
out_ << results[0] << " = ca_.UncheckedCast<"
<< return_type->GetGeneratedTNodeTypeName() << ">(";
} else {
DCHECK_EQ(0, results.size());
}
}
out_ << instruction.macro->external_assembler_name() << "(state_)."
<< instruction.macro->ExternalName() << "(";
PrintCommaSeparatedList(out_, args);
if (return_type->IsStructType()) {
if (needs_flattening) {
out_ << ").Flatten();\n";
} else {
if (results.size() == 1) out_ << ")";
......@@ -695,69 +699,49 @@ void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
}
void CSAGenerator::EmitInstruction(
const LoadObjectFieldInstruction& instruction, Stack<std::string>* stack) {
const CreateFieldReferenceInstruction& instruction,
Stack<std::string>* stack) {
const Field& field =
instruction.class_type->LookupField(instruction.field_name);
std::string result_name = FreshNodeName();
size_t field_size;
std::string size_string;
std::string machine_type;
std::tie(field_size, size_string, machine_type) =
field.GetFieldSizeInformation();
std::string offset_name = FreshNodeName();
stack->Push(offset_name);
out_ << " compiler::TNode<IntPtrT> " << offset_name
<< " = ca_.IntPtrConstant(";
if (instruction.class_type->IsExtern()) {
out_ << field.name_and_type.type->GetGeneratedTypeName() << " "
<< result_name << " = ca_.UncheckedCast<"
<< field.name_and_type.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).LoadObjectField(" << stack->Top()
<< ", " << field.aggregate->GetGeneratedTNodeTypeName() << "::k"
<< CamelifyString(field.name_and_type.name) << "Offset, "
<< machine_type + "));\n";
out_ << field.aggregate->GetGeneratedTNodeTypeName() << "::k"
<< CamelifyString(field.name_and_type.name) << "Offset";
} else {
out_ << field.name_and_type.type->GetGeneratedTypeName() << " "
<< result_name << " = ca_.UncheckedCast<"
<< field.name_and_type.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler(state_).UnsafeLoadFixedArrayElement("
<< stack->Top() << ", " << (field.offset / kTaggedSize) << "));\n";
out_ << "FixedArray::kHeaderSize + " << field.offset;
}
stack->Poke(stack->AboveTop() - 1, result_name);
out_ << ");\n"
<< " USE(" << stack->Top() << ");\n";
}
void CSAGenerator::EmitInstruction(
const StoreObjectFieldInstruction& instruction, Stack<std::string>* stack) {
auto value = stack->Pop();
auto object = stack->Pop();
stack->Push(value);
const Field& field =
instruction.class_type->LookupField(instruction.field_name);
if (instruction.class_type->IsExtern()) {
if (field.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
if (field.offset == 0) {
out_ << " CodeStubAssembler(state_).StoreMap(" << object << ", "
<< value << ");\n";
} else {
out_ << " CodeStubAssembler(state_).StoreObjectField(" << object
<< ", " << field.offset << ", " << value << ");\n";
}
} else {
size_t field_size;
std::string size_string;
std::string machine_type;
std::tie(field_size, size_string, machine_type) =
field.GetFieldSizeInformation();
if (field.offset == 0) {
ReportError("the first field in a class object must be a map");
}
out_ << " CodeStubAssembler(state_).StoreObjectFieldNoWriteBarrier("
<< object << ", " << field.offset << ", " << value << ", "
<< machine_type << ".representation());\n";
}
} else {
out_ << " CodeStubAssembler(state_).UnsafeStoreFixedArrayElement("
<< object << ", " << (field.offset / kTaggedSize) << ", " << value
<< ");\n";
}
void CSAGenerator::EmitInstruction(const LoadReferenceInstruction& instruction,
Stack<std::string>* stack) {
std::string result_name = FreshNodeName();
std::string offset = stack->Pop();
std::string object = stack->Pop();
stack->Push(result_name);
out_ << " " << instruction.type->GetGeneratedTypeName() << result_name
<< " = CodeStubAssembler(state_).LoadReference<"
<< instruction.type->GetGeneratedTNodeTypeName()
<< ">(CodeStubAssembler::Reference{" << object << ", " << offset
<< "});\n";
}
void CSAGenerator::EmitInstruction(const StoreReferenceInstruction& instruction,
Stack<std::string>* stack) {
std::string value = stack->Pop();
std::string offset = stack->Pop();
std::string object = stack->Pop();
out_ << " CodeStubAssembler(state_).StoreReference(CodeStubAssembler::"
"Reference{"
<< object << ", " << offset << "}, " << value << ");\n";
}
// static
......@@ -778,6 +762,11 @@ void CSAGenerator::EmitCSAValue(VisitResult result,
out);
}
out << "}";
} else if (result.type()->IsReferenceType()) {
DCHECK_EQ(2, result.stack_range().Size());
size_t offset = result.stack_range().begin().offset;
out << "CodeStubAssembler::Reference{" << values.Peek(BottomOffset{offset})
<< ", " << values.Peek(BottomOffset{offset + 1}) << "}";
} else {
DCHECK_EQ(1, result.stack_range().Size());
out << "compiler::TNode<" << result.type()->GetGeneratedTNodeTypeName()
......
......@@ -64,23 +64,37 @@ std::ostream& operator<<(std::ostream& os, const Generic& g) {
return os;
}
base::Optional<const Type*> Generic::InferTypeArgument(
size_t i, const TypeVector& arguments) {
const std::string type_name = declaration()->generic_parameters[i]->value;
const std::vector<TypeExpression*>& parameters =
declaration()->callable->signature->parameters.types;
size_t j = declaration()->callable->signature->parameters.implicit_count;
for (size_t i = 0; i < arguments.size() && j < parameters.size(); ++i, ++j) {
BasicTypeExpression* basic =
BasicTypeExpression::DynamicCast(parameters[j]);
if (basic && basic->namespace_qualification.empty() &&
!basic->is_constexpr && basic->name == type_name) {
return arguments[i];
namespace {
base::Optional<const Type*> InferTypeArgument(const std::string& to_infer,
TypeExpression* parameter,
const Type* argument) {
BasicTypeExpression* basic = BasicTypeExpression::DynamicCast(parameter);
if (basic && basic->namespace_qualification.empty() && !basic->is_constexpr &&
basic->name == to_infer) {
return argument;
}
auto* ref = ReferenceTypeExpression::DynamicCast(parameter);
if (ref && argument->IsReferenceType()) {
return InferTypeArgument(to_infer, ref->referenced_type,
ReferenceType::cast(argument)->referenced_type());
}
return base::nullopt;
}
base::Optional<const Type*> InferTypeArgument(
const std::string& to_infer, const std::vector<TypeExpression*>& parameters,
const TypeVector& arguments) {
for (size_t i = 0; i < arguments.size() && i < parameters.size(); ++i) {
if (base::Optional<const Type*> inferred =
InferTypeArgument(to_infer, parameters[i], arguments[i])) {
return *inferred;
}
}
return base::nullopt;
}
} // namespace
base::Optional<TypeVector> Generic::InferSpecializationTypes(
const TypeVector& explicit_specialization_types,
const TypeVector& arguments) {
......@@ -91,7 +105,15 @@ base::Optional<TypeVector> Generic::InferSpecializationTypes(
}
for (size_t i = explicit_specialization_types.size();
i < type_parameter_count; ++i) {
base::Optional<const Type*> inferred = InferTypeArgument(i, arguments);
const std::string type_name = declaration()->generic_parameters[i]->value;
size_t implicit_count =
declaration()->callable->signature->parameters.implicit_count;
const std::vector<TypeExpression*>& parameters =
declaration()->callable->signature->parameters.types;
std::vector<TypeExpression*> explicit_parameters(
parameters.begin() + implicit_count, parameters.end());
base::Optional<const Type*> inferred =
InferTypeArgument(type_name, explicit_parameters, arguments);
if (!inferred) return base::nullopt;
result.push_back(*inferred);
}
......
......@@ -421,8 +421,6 @@ class Generic : public Declarable {
: Declarable(Declarable::kGeneric),
name_(name),
declaration_(declaration) {}
base::Optional<const Type*> InferTypeArgument(size_t i,
const TypeVector& arguments);
std::string name_;
std::unordered_map<TypeVector, Callable*, base::hash<TypeVector>>
......
......@@ -561,24 +561,22 @@ void DeclarationVisitor::FinalizeClassFieldsAndMethods(
std::string camel_field_name = CamelifyString(field.name_and_type.name);
std::string load_macro_name =
"Load" + class_type->name() + camel_field_name;
std::string load_operator_name = "." + field.name_and_type.name;
Signature load_signature;
load_signature.parameter_names.push_back(MakeNode<Identifier>("o"));
load_signature.parameter_types.types.push_back(class_type);
load_signature.parameter_types.var_args = false;
load_signature.return_type = field.name_and_type.type;
Statement* load_body =
MakeNode<ReturnStatement>(MakeNode<LoadObjectFieldExpression>(
parameter, field.name_and_type.name));
MakeNode<ReturnStatement>(MakeNode<FieldAccessExpression>(
parameter, MakeNode<Identifier>(field.name_and_type.name)));
Declarations::DeclareMacro(load_macro_name, base::nullopt, load_signature,
false, load_body, load_operator_name);
false, load_body);
// Store accessor
IdentifierExpression* value = MakeNode<IdentifierExpression>(
std::vector<std::string>{}, MakeNode<Identifier>(std::string{"v"}));
std::string store_macro_name =
"Store" + class_type->name() + camel_field_name;
std::string store_operator_name = "." + field.name_and_type.name + "=";
Signature store_signature;
store_signature.parameter_names.push_back(MakeNode<Identifier>("o"));
store_signature.parameter_names.push_back(MakeNode<Identifier>("v"));
......@@ -588,10 +586,12 @@ void DeclarationVisitor::FinalizeClassFieldsAndMethods(
// TODO(danno): Store macros probably should return their value argument
store_signature.return_type = TypeOracle::GetVoidType();
Statement* store_body =
MakeNode<ExpressionStatement>(MakeNode<StoreObjectFieldExpression>(
parameter, field.name_and_type.name, value));
MakeNode<ExpressionStatement>(MakeNode<AssignmentExpression>(
MakeNode<FieldAccessExpression>(
parameter, MakeNode<Identifier>(field.name_and_type.name)),
value));
Declarations::DeclareMacro(store_macro_name, base::nullopt, store_signature,
false, store_body, store_operator_name);
false, store_body);
}
DeclareMethods(class_type, class_declaration->methods);
......
......@@ -91,9 +91,14 @@ const Type* Declarations::GetType(TypeExpression* type_expression) {
alias->GetDeclarationPosition());
}
return alias->type();
} else if (auto* union_type = UnionTypeExpression::cast(type_expression)) {
} else if (auto* union_type =
UnionTypeExpression::DynamicCast(type_expression)) {
return TypeOracle::GetUnionType(GetType(union_type->a),
GetType(union_type->b));
} else if (auto* reference_type =
ReferenceTypeExpression::DynamicCast(type_expression)) {
return TypeOracle::GetReferenceType(
GetType(reference_type->referenced_type));
} else {
auto* function_type_exp = FunctionTypeExpression::cast(type_expression);
TypeVector argument_types;
......
......@@ -46,7 +46,6 @@ enum class ParseResultHolderBase::TypeId {
kExpressionPtr,
kIdentifierPtr,
kOptionalIdentifierPtr,
kLocationExpressionPtr,
kStatementPtr,
kDeclarationPtr,
kTypeExpressionPtr,
......
This diff is collapsed.
......@@ -41,6 +41,14 @@ class LocationReference {
result.temporary_description_ = std::move(description);
return result;
}
// A heap reference, that is, a tagged value and an offset to encode an inner
// pointer.
static LocationReference HeapReference(VisitResult heap_reference) {
LocationReference result;
DCHECK(heap_reference.type()->IsReferenceType());
result.heap_reference_ = std::move(heap_reference);
return result;
}
static LocationReference ArrayAccess(VisitResult base, VisitResult offset) {
LocationReference result;
result.eval_function_ = std::string{"[]"};
......@@ -89,6 +97,18 @@ class LocationReference {
DCHECK(IsTemporary());
return *temporary_;
}
bool IsHeapReference() const { return heap_reference_.has_value(); }
const VisitResult& heap_reference() const {
DCHECK(IsHeapReference());
return *heap_reference_;
}
const Type* ReferencedType() const {
if (IsHeapReference()) {
return ReferenceType::cast(heap_reference().type())->referenced_type();
}
return GetVisitResult().type();
}
const VisitResult& GetVisitResult() const {
if (IsVariableAccess()) return variable();
......@@ -131,6 +151,7 @@ class LocationReference {
base::Optional<VisitResult> variable_;
base::Optional<VisitResult> temporary_;
base::Optional<std::string> temporary_description_;
base::Optional<VisitResult> heap_reference_;
base::Optional<std::string> eval_function_;
base::Optional<std::string> assign_function_;
VisitResultVector call_arguments_;
......@@ -287,6 +308,7 @@ class ImplementationVisitor : public FileVisitor {
LocationReference GetLocationReference(Expression* location);
LocationReference GetLocationReference(IdentifierExpression* expr);
LocationReference GetLocationReference(DereferenceExpression* expr);
LocationReference GetLocationReference(FieldAccessExpression* expr);
LocationReference GetLocationReference(ElementAccessExpression* expr);
......@@ -294,15 +316,7 @@ class ImplementationVisitor : public FileVisitor {
VisitResult GetBuiltinCode(Builtin* builtin);
VisitResult Visit(IdentifierExpression* expr);
VisitResult Visit(FieldAccessExpression* expr) {
StackScope scope(this);
return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
}
VisitResult Visit(ElementAccessExpression* expr) {
StackScope scope(this);
return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
}
VisitResult Visit(LocationExpression* expr);
void VisitAllDeclarables();
void Visit(Declarable* delarable);
......@@ -320,8 +334,6 @@ class ImplementationVisitor : public FileVisitor {
VisitResult Visit(CallExpression* expr, bool is_tail = false);
VisitResult Visit(CallMethodExpression* expr);
VisitResult Visit(IntrinsicCallExpression* intrinsic);
VisitResult Visit(LoadObjectFieldExpression* expr);
VisitResult Visit(StoreObjectFieldExpression* expr);
const Type* Visit(TailCallStatement* stmt);
VisitResult Visit(ConditionalExpression* expr);
......@@ -465,7 +477,10 @@ class ImplementationVisitor : public FileVisitor {
const Container& declaration_container,
const TypeVector& types,
const std::vector<Binding<LocalLabel>*>& labels,
const TypeVector& specialization_types);
const TypeVector& specialization_types,
bool silence_errors = false);
bool TestLookupCallable(const QualifiedName& name,
const TypeVector& parameter_types);
template <class Container>
Callable* LookupCallable(const QualifiedName& name,
......
......@@ -21,6 +21,19 @@ namespace torque {
TORQUE_INSTRUCTION_LIST(TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS)
#undef TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS
namespace {
void ExpectType(const Type* expected, const Type* actual) {
if (expected != actual) {
ReportError("expected type ", *expected, " but found ", *actual);
}
}
void ExpectSubtype(const Type* subtype, const Type* supertype) {
if (!subtype->IsSubtypeOf(supertype)) {
ReportError("type ", *subtype, " is not a subtype of ", *supertype);
}
}
} // namespace
void PeekInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const Type* type = stack->Peek(slot);
......@@ -29,9 +42,7 @@ void PeekInstruction::TypeInstruction(Stack<const Type*>* stack,
const TopType* top_type = TopType::cast(type);
ReportError("use of " + top_type->reason());
}
if (!type->IsSubtypeOf(*widened_type)) {
ReportError("type ", *type, " is not a subtype of ", **widened_type);
}
ExpectSubtype(type, *widened_type);
type = *widened_type;
}
stack->Push(type);
......@@ -41,9 +52,7 @@ void PokeInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const Type* type = stack->Top();
if (widened_type) {
if (!type->IsSubtypeOf(*widened_type)) {
ReportError("type ", type, " is not a subtype of ", *widened_type);
}
ExpectSubtype(type, *widened_type);
type = *widened_type;
}
stack->Poke(slot, type);
......@@ -281,39 +290,26 @@ void UnsafeCastInstruction::TypeInstruction(Stack<const Type*>* stack,
stack->Poke(stack->AboveTop() - 1, destination_type);
}
void LoadObjectFieldInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const ClassType* stack_class_type = ClassType::DynamicCast(stack->Top());
if (!stack_class_type) {
ReportError(
"first argument to a LoadObjectFieldInstruction instruction isn't a "
"class");
}
if (stack_class_type != class_type) {
ReportError(
"first argument to a LoadObjectFieldInstruction doesn't match "
"instruction's type");
}
const Field& field = class_type->LookupField(field_name);
stack->Poke(stack->AboveTop() - 1, field.name_and_type.type);
void CreateFieldReferenceInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
ExpectSubtype(stack->Pop(), class_type);
stack->Push(TypeOracle::GetHeapObjectType());
stack->Push(TypeOracle::GetIntPtrType());
}
void StoreObjectFieldInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
auto value = stack->Pop();
const ClassType* stack_class_type = ClassType::DynamicCast(stack->Top());
if (!stack_class_type) {
ReportError(
"first argument to a StoreObjectFieldInstruction instruction isn't a "
"class");
}
if (stack_class_type != class_type) {
ReportError(
"first argument to a StoreObjectFieldInstruction doesn't match "
"instruction's type");
}
stack->Pop();
stack->Push(value);
void LoadReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
ExpectType(TypeOracle::GetHeapObjectType(), stack->Pop());
DCHECK_EQ(std::vector<const Type*>{type}, LowerType(type));
stack->Push(type);
}
void StoreReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectSubtype(stack->Pop(), type);
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
ExpectType(TypeOracle::GetHeapObjectType(), stack->Pop());
}
bool CallRuntimeInstruction::IsBlockTerminator() const {
......
......@@ -30,8 +30,9 @@ class RuntimeFunction;
V(DeleteRangeInstruction) \
V(PushUninitializedInstruction) \
V(PushBuiltinPointerInstruction) \
V(LoadObjectFieldInstruction) \
V(StoreObjectFieldInstruction) \
V(CreateFieldReferenceInstruction) \
V(LoadReferenceInstruction) \
V(StoreReferenceInstruction) \
V(CallCsaMacroInstruction) \
V(CallIntrinsicInstruction) \
V(NamespaceConstantInstruction) \
......@@ -203,22 +204,25 @@ struct NamespaceConstantInstruction : InstructionBase {
NamespaceConstant* constant;
};
struct LoadObjectFieldInstruction : InstructionBase {
struct CreateFieldReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
LoadObjectFieldInstruction(const ClassType* class_type,
std::string field_name)
CreateFieldReferenceInstruction(const ClassType* class_type,
std::string field_name)
: class_type(class_type), field_name(std::move(field_name)) {}
const ClassType* class_type;
std::string field_name;
};
struct StoreObjectFieldInstruction : InstructionBase {
struct LoadReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
StoreObjectFieldInstruction(const ClassType* class_type,
std::string field_name)
: class_type(class_type), field_name(std::move(field_name)) {}
const ClassType* class_type;
std::string field_name;
explicit LoadReferenceInstruction(const Type* type) : type(type) {}
const Type* type;
};
struct StoreReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
explicit StoreReferenceInstruction(const Type* type) : type(type) {}
const Type* type;
};
struct CallIntrinsicInstruction : InstructionBase {
......
......@@ -68,10 +68,6 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<base::Optional<Identifier*>>::id =
ParseResultTypeId::kOptionalIdentifierPtr;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<LocationExpression*>::id =
ParseResultTypeId::kLocationExpressionPtr;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<Statement*>::id =
ParseResultTypeId::kStatementPtr;
template <>
......@@ -261,7 +257,7 @@ Expression* MakeCall(const std::string& callee,
}
base::Optional<ParseResult> MakeCall(ParseResultIterator* child_results) {
auto callee = child_results->NextAs<LocationExpression*>();
auto callee = child_results->NextAs<Expression*>();
auto args = child_results->NextAs<std::vector<Expression*>>();
auto otherwise = child_results->NextAs<std::vector<Statement*>>();
IdentifierExpression* target = IdentifierExpression::cast(callee);
......@@ -694,6 +690,13 @@ base::Optional<ParseResult> MakeFunctionTypeExpression(
return ParseResult{result};
}
base::Optional<ParseResult> MakeReferenceTypeExpression(
ParseResultIterator* child_results) {
auto referenced_type = child_results->NextAs<TypeExpression*>();
TypeExpression* result = MakeNode<ReferenceTypeExpression>(referenced_type);
return ParseResult{result};
}
base::Optional<ParseResult> MakeUnionTypeExpression(
ParseResultIterator* child_results) {
auto a = child_results->NextAs<TypeExpression*>();
......@@ -1001,7 +1004,7 @@ base::Optional<ParseResult> MakeIdentifierExpression(
auto name = child_results->NextAs<Identifier*>();
auto generic_arguments =
child_results->NextAs<std::vector<TypeExpression*>>();
LocationExpression* result = MakeNode<IdentifierExpression>(
Expression* result = MakeNode<IdentifierExpression>(
std::move(namespace_qualification), name, std::move(generic_arguments));
return ParseResult{result};
}
......@@ -1010,7 +1013,7 @@ base::Optional<ParseResult> MakeFieldAccessExpression(
ParseResultIterator* child_results) {
auto object = child_results->NextAs<Expression*>();
auto field = child_results->NextAs<Identifier*>();
LocationExpression* result = MakeNode<FieldAccessExpression>(object, field);
Expression* result = MakeNode<FieldAccessExpression>(object, field);
return ParseResult{result};
}
......@@ -1018,7 +1021,14 @@ base::Optional<ParseResult> MakeElementAccessExpression(
ParseResultIterator* child_results) {
auto object = child_results->NextAs<Expression*>();
auto field = child_results->NextAs<Expression*>();
LocationExpression* result = MakeNode<ElementAccessExpression>(object, field);
Expression* result = MakeNode<ElementAccessExpression>(object, field);
return ParseResult{result};
}
base::Optional<ParseResult> MakeDereferenceExpression(
ParseResultIterator* child_results) {
auto reference = child_results->NextAs<Expression*>();
Expression* result = MakeNode<DereferenceExpression>(reference);
return ParseResult{result};
}
......@@ -1033,7 +1043,7 @@ base::Optional<ParseResult> MakeStructExpression(
base::Optional<ParseResult> MakeAssignmentExpression(
ParseResultIterator* child_results) {
auto location = child_results->NextAs<LocationExpression*>();
auto location = child_results->NextAs<Expression*>();
auto op = child_results->NextAs<base::Optional<std::string>>();
auto value = child_results->NextAs<Expression*>();
Expression* result =
......@@ -1057,7 +1067,7 @@ base::Optional<ParseResult> MakeStringLiteralExpression(
base::Optional<ParseResult> MakeIncrementDecrementExpressionPostfix(
ParseResultIterator* child_results) {
auto location = child_results->NextAs<LocationExpression*>();
auto location = child_results->NextAs<Expression*>();
auto op = child_results->NextAs<IncrementDecrementOperator>();
Expression* result =
MakeNode<IncrementDecrementExpression>(location, op, true);
......@@ -1067,7 +1077,7 @@ base::Optional<ParseResult> MakeIncrementDecrementExpressionPostfix(
base::Optional<ParseResult> MakeIncrementDecrementExpressionPrefix(
ParseResultIterator* child_results) {
auto op = child_results->NextAs<IncrementDecrementOperator>();
auto location = child_results->NextAs<LocationExpression*>();
auto location = child_results->NextAs<Expression*>();
Expression* result =
MakeNode<IncrementDecrementExpression>(location, op, false);
return ParseResult{result};
......@@ -1284,7 +1294,8 @@ struct TorqueGrammar : Grammar {
MakeBasicTypeExpression),
Rule({Token("builtin"), Token("("), typeList, Token(")"), Token("=>"),
&simpleType},
MakeFunctionTypeExpression)};
MakeFunctionTypeExpression),
Rule({Token("&"), &simpleType}, MakeReferenceTypeExpression)};
// Result: TypeExpression*
Symbol type = {Rule({&simpleType}), Rule({&type, Token("|"), &simpleType},
......@@ -1397,20 +1408,13 @@ struct TorqueGrammar : Grammar {
YieldIntegralConstant<IncrementDecrementOperator,
IncrementDecrementOperator::kDecrement>)};
// Result: LocationExpression*
// Result: Expression*
Symbol identifierExpression = {
Rule({List<std::string>(Sequence({&identifier, Token("::")})), &name,
TryOrDefault<TypeList>(&genericSpecializationTypeList)},
MakeIdentifierExpression),
};
// Result: LocationExpression*
Symbol locationExpression = {
Rule({&identifierExpression}),
Rule({&primaryExpression, Token("."), &name}, MakeFieldAccessExpression),
Rule({&primaryExpression, Token("["), expression, Token("]")},
MakeElementAccessExpression)};
// Result: std::vector<Expression*>
Symbol argumentList = {Rule(
{Token("("), List<Expression*>(expression, Token(",")), Token(")")})};
......@@ -1446,8 +1450,10 @@ struct TorqueGrammar : Grammar {
Rule({&callExpression}),
Rule({&callMethodExpression}),
Rule({&intrinsicCallExpression}),
Rule({&locationExpression},
CastParseResult<LocationExpression*, Expression*>),
Rule({&identifierExpression}),
Rule({&primaryExpression, Token("."), &name}, MakeFieldAccessExpression),
Rule({&primaryExpression, Token("["), expression, Token("]")},
MakeElementAccessExpression),
Rule({&decimalLiteral}, MakeNumberLiteralExpression),
Rule({&stringLiteral}, MakeStringLiteralExpression),
Rule({&simpleType, &initializerList}, MakeStructExpression),
......@@ -1457,11 +1463,13 @@ struct TorqueGrammar : Grammar {
// Result: Expression*
Symbol unaryExpression = {
Rule({&primaryExpression}),
Rule({OneOf({"+", "-", "!", "~"}), &unaryExpression}, MakeUnaryOperator),
Rule({OneOf({"+", "-", "!", "~", "&"}), &unaryExpression},
MakeUnaryOperator),
Rule({Token("*"), &unaryExpression}, MakeDereferenceExpression),
Rule({Token("..."), &unaryExpression}, MakeSpreadExpression),
Rule({&incrementDecrementOperator, &locationExpression},
Rule({&incrementDecrementOperator, &unaryExpression},
MakeIncrementDecrementExpressionPrefix),
Rule({&locationExpression, &incrementDecrementOperator},
Rule({&unaryExpression, &incrementDecrementOperator},
MakeIncrementDecrementExpressionPostfix)};
// Result: Expression*
......@@ -1521,7 +1529,7 @@ struct TorqueGrammar : Grammar {
// Result: Expression*
Symbol assignmentExpression = {
Rule({&conditionalExpression}),
Rule({&locationExpression, &assignmentOperator, &assignmentExpression},
Rule({&conditionalExpression, &assignmentOperator, &assignmentExpression},
MakeAssignmentExpression)};
// Result: Statement*
......
......@@ -58,6 +58,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return result;
}
static const ReferenceType* GetReferenceType(const Type* referenced_type) {
return Get().reference_types_.Add(ReferenceType(referenced_type));
}
static const std::vector<const BuiltinPointerType*>&
AllBuiltinPointerTypes() {
return Get().all_builtin_pointer_types_;
......@@ -117,6 +121,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(OBJECT_TYPE_STRING);
}
static const Type* GetHeapObjectType() {
return Get().GetBuiltinType(HEAP_OBJECT_TYPE_STRING);
}
static const Type* GetJSObjectType() {
return Get().GetBuiltinType(JSOBJECT_TYPE_STRING);
}
......@@ -207,6 +215,7 @@ class TypeOracle : public ContextualClass<TypeOracle> {
Deduplicator<BuiltinPointerType> function_pointer_types_;
std::vector<const BuiltinPointerType*> all_builtin_pointer_types_;
Deduplicator<UnionType> union_types_;
Deduplicator<ReferenceType> reference_types_;
std::vector<std::unique_ptr<Type>> nominal_types_;
std::vector<std::unique_ptr<Type>> struct_types_;
std::vector<std::unique_ptr<Type>> top_types_;
......
......@@ -46,6 +46,13 @@ bool Type::IsSubtypeOf(const Type* supertype) const {
return false;
}
base::Optional<const ClassType*> Type::ClassSupertype() const {
for (const Type* t = this; t != nullptr; t = t->parent()) {
if (auto* class_type = ClassType::DynamicCast(t)) return class_type;
}
return base::nullopt;
}
// static
const Type* Type::CommonSupertype(const Type* a, const Type* b) {
int diff = a->Depth() - b->Depth();
......@@ -158,19 +165,6 @@ std::string UnionType::GetGeneratedTNodeTypeNameImpl() const {
return parent()->GetGeneratedTNodeTypeName();
}
const Type* UnionType::NonConstexprVersion() const {
if (IsConstexpr()) {
auto it = types_.begin();
UnionType result((*it)->NonConstexprVersion());
++it;
for (; it != types_.end(); ++it) {
result.Extend((*it)->NonConstexprVersion());
}
return TypeOracle::GetUnionType(std::move(result));
}
return this;
}
void UnionType::RecomputeParent() {
const Type* parent = nullptr;
for (const Type* t : types_) {
......@@ -470,6 +464,9 @@ void AppendLoweredTypes(const Type* type, std::vector<const Type*>* result) {
for (const Field& field : s->fields()) {
AppendLoweredTypes(field.name_and_type.type, result);
}
} else if (type->IsReferenceType()) {
result->push_back(TypeOracle::GetHeapObjectType());
result->push_back(TypeOracle::GetIntPtrType());
} else {
result->push_back(type);
}
......
......@@ -29,6 +29,7 @@ static const char* const ARGUMENTS_TYPE_STRING = "Arguments";
static const char* const CONTEXT_TYPE_STRING = "Context";
static const char* const MAP_TYPE_STRING = "Map";
static const char* const OBJECT_TYPE_STRING = "Object";
static const char* const HEAP_OBJECT_TYPE_STRING = "HeapObject";
static const char* const JSOBJECT_TYPE_STRING = "JSObject";
static const char* const SMI_TYPE_STRING = "Smi";
static const char* const TAGGED_TYPE_STRING = "Tagged";
......@@ -65,6 +66,7 @@ class TypeBase {
kTopType,
kAbstractType,
kBuiltinPointerType,
kReferenceType,
kUnionType,
kStructType,
kClassType
......@@ -75,6 +77,7 @@ class TypeBase {
bool IsBuiltinPointerType() const {
return kind() == Kind::kBuiltinPointerType;
}
bool IsReferenceType() const { return kind() == Kind::kReferenceType; }
bool IsUnionType() const { return kind() == Kind::kUnionType; }
bool IsStructType() const { return kind() == Kind::kStructType; }
bool IsClassType() const { return kind() == Kind::kClassType; }
......@@ -123,9 +126,13 @@ class Type : public TypeBase {
bool IsVoidOrNever() const { return IsVoid() || IsNever(); }
std::string GetGeneratedTypeName() const;
std::string GetGeneratedTNodeTypeName() const;
virtual bool IsConstexpr() const = 0;
virtual bool IsConstexpr() const {
if (parent()) DCHECK(!parent()->IsConstexpr());
return false;
}
virtual bool IsTransient() const { return false; }
virtual const Type* NonConstexprVersion() const = 0;
virtual const Type* NonConstexprVersion() const { return this; }
base::Optional<const ClassType*> ClassSupertype() const;
static const Type* CommonSupertype(const Type* a, const Type* b);
void AddAlias(std::string alias) const { aliases_.insert(std::move(alias)); }
......@@ -189,8 +196,6 @@ class TopType final : public Type {
std::string GetGeneratedTNodeTypeNameImpl() const override {
return source_type_->GetGeneratedTNodeTypeName();
}
bool IsConstexpr() const override { return false; }
const Type* NonConstexprVersion() const override { return nullptr; }
std::string ToExplicitString() const override {
std::stringstream s;
s << "inaccessible " + source_type_->ToString();
......@@ -268,11 +273,6 @@ class BuiltinPointerType final : public Type {
std::string GetGeneratedTNodeTypeNameImpl() const override {
return parent()->GetGeneratedTNodeTypeName();
}
bool IsConstexpr() const override {
DCHECK(!parent()->IsConstexpr());
return false;
}
const Type* NonConstexprVersion() const override { return this; }
const TypeVector& parameter_types() const { return parameter_types_; }
const Type* return_type() const { return return_type_; }
......@@ -304,6 +304,43 @@ class BuiltinPointerType final : public Type {
const size_t function_pointer_type_id_;
};
class ReferenceType final : public Type {
public:
DECLARE_TYPE_BOILERPLATE(ReferenceType)
std::string MangledName() const override {
return "RT" + referenced_type_->MangledName();
}
std::string ToExplicitString() const override {
std::string s = referenced_type_->ToString();
if (s.find(' ') != std::string::npos) {
s = "(" + s + ")";
}
return "&" + s;
}
std::string GetGeneratedTypeNameImpl() const override {
return "CodeStubAssembler::Reference";
}
std::string GetGeneratedTNodeTypeNameImpl() const override { UNREACHABLE(); }
const Type* referenced_type() const { return referenced_type_; }
friend size_t hash_value(const ReferenceType& p) {
return base::hash_combine(static_cast<size_t>(Kind::kReferenceType),
p.referenced_type_);
}
bool operator==(const ReferenceType& other) const {
return referenced_type_ == other.referenced_type_;
}
private:
friend class TypeOracle;
explicit ReferenceType(const Type* referenced_type)
: Type(Kind::kReferenceType, nullptr),
referenced_type_(referenced_type) {}
const Type* const referenced_type_;
};
bool operator<(const Type& a, const Type& b);
struct TypeLess {
bool operator()(const Type* const a, const Type* const b) const {
......@@ -321,12 +358,6 @@ class UnionType final : public Type {
}
std::string GetGeneratedTNodeTypeNameImpl() const override;
bool IsConstexpr() const override {
DCHECK_EQ(false, parent()->IsConstexpr());
return false;
}
const Type* NonConstexprVersion() const override;
friend size_t hash_value(const UnionType& p) {
size_t result = 0;
for (const Type* t : p.types_) {
......@@ -407,9 +438,7 @@ class AggregateType : public Type {
std::string MangledName() const override { return name_; }
std::string GetGeneratedTypeNameImpl() const override { UNREACHABLE(); }
std::string GetGeneratedTNodeTypeNameImpl() const override { UNREACHABLE(); }
const Type* NonConstexprVersion() const override { return this; }
bool IsConstexpr() const override { return false; }
virtual bool HasIndexedField() const { return false; }
void SetFields(std::vector<Field> fields) { fields_ = std::move(fields); }
......
......@@ -472,6 +472,20 @@ TEST(TestNewFixedArrayFromSpread) {
ft.Call();
}
TEST(TestReferences) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestReferences();
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -858,4 +858,28 @@ namespace test {
const i = TestIterator{count: 5};
return new FixedArray{map: kFixedArrayMap, length: 5, objects: ...i};
}
class SmiPair {
GetA():&Smi {
return & this.a;
}
a: Smi;
b: Smi;
}
macro Swap<T: type>(a:&T, b:&T) {
const tmp = * a;
* a = * b;
* b = tmp;
}
macro TestReferences() {
const array = new SmiPair{a: 7, b: 2};
const ref:&Smi = & array.a;
* ref = 3 + * ref;
-- * ref;
Swap(& array.b, array.GetA());
check(array.a == 2);
check(array.b == 9);
}
}
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