Commit 26e39d12 authored by Georg Schmid's avatar Georg Schmid Committed by Commit Bot

[torque] Add user-defined Slice struct

This CL consists of several preparatory steps for slices in Torque. Above all, it introduces a user-defined struct, torque_internal::Slice<T>, that performs bounds checking and returns references to elements in arrays. To enable this, several smaller changes were also made:

- Constructors of internal classes such as torque_internal::Reference<T> now require a special 'Unsafe' argument, making it clear that there be dragons.
- Struct methods are now declared during finalization. This allows instances of generic structs to have methods referring to the same struct. Previously, methods would be declared before the instance had been fully registered, leading to errors during type resolution. Furthermore, such methods were declared in a temporary namespace, that would then erroneously escape and lead to use-after-free issues.
- Instances of TypeArgumentInference were not running in the correct (Torque) scopes, leading to type resolution errors.
- The chain of ContextualVariable::Scope for any given ContextualVariable (such as CurrentScope) can now be walked, simplifying debugging.

R=jgruber@chromium.org, tebbi@chromium.org

Bug: v8:7793
Change-Id: I36f808f63cc3ce441062dfc56f511f24f1e3121e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1758322
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63314}
parent 1eda962e
...@@ -992,6 +992,8 @@ extern class WasmDebugInfo extends Struct { ...@@ -992,6 +992,8 @@ extern class WasmDebugInfo extends Struct {
extern class WasmExceptionTag extends Struct { index: Smi; } extern class WasmExceptionTag extends Struct { index: Smi; }
const kTaggedSize: constexpr int31 generates 'kTaggedSize';
const kSmiTagSize: constexpr int31 generates 'kSmiTagSize'; const kSmiTagSize: constexpr int31 generates 'kSmiTagSize';
const V8_INFINITY: constexpr float64 generates 'V8_INFINITY'; const V8_INFINITY: constexpr float64 generates 'V8_INFINITY';
......
...@@ -3,10 +3,87 @@ ...@@ -3,10 +3,87 @@
// found in the LICENSE file. // found in the LICENSE file.
namespace torque_internal { namespace torque_internal {
// TODO(gsps): Synthesize SizeOf<T> in the compiler
macro SizeOf<T: type>(): constexpr int31;
SizeOf<Smi>(): constexpr int31 {
return kTaggedSize;
}
// Unsafe is a marker that we require to be passed when calling internal APIs
// // that might lead to unsoundness when used incorrectly. Unsafe markers
// should therefore not be instantiated anywhere outside of this namespace.
struct Unsafe {}
struct Reference<T: type> { struct Reference<T: type> {
const object: HeapObject; const object: HeapObject;
const offset: intptr; const offset: intptr;
unsafeMarker: Unsafe;
}
macro UnsafeNewReference<T: type>(object: HeapObject, offset: intptr):&T {
return Reference<T>{
object: object,
offset: offset,
unsafeMarker: Unsafe {}
};
}
struct Slice<T: type> {
Access(index: intptr):&T labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return UnsafeNewReference<T>(
this.object, this.offset + index * SizeOf<T>());
} else {
goto OutOfBounds;
}
}
Iterator(): SliceIterator<T> {
const end = this.offset + this.length * SizeOf<T>();
return SliceIterator<T>{
object: this.object,
start: this.offset,
end: end,
unsafeMarker: Unsafe {}
};
}
const object: HeapObject;
const offset: intptr;
const length: intptr;
unsafeMarker: Unsafe;
}
macro UnsafeNewSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): Slice<T> {
return Slice<T>{
object: object,
offset: offset,
length: length,
unsafeMarker: Unsafe {}
};
}
struct SliceIterator<T: type> {
Empty(): bool {
return this.start == this.end;
}
Next():&T labels NoMore {
if (this.Empty()) {
goto NoMore;
} else {
const result = UnsafeNewReference<T>(this.object, this.start);
this.start += SizeOf<T>();
return result;
}
}
object: HeapObject;
start: intptr;
end: intptr;
unsafeMarker: Unsafe;
} }
} // namespace torque_internal } // namespace torque_internal
...@@ -51,6 +51,7 @@ static const char* const CONST_INT32_TYPE_STRING = "constexpr int32"; ...@@ -51,6 +51,7 @@ static const char* const CONST_INT32_TYPE_STRING = "constexpr int32";
static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64"; static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64";
static const char* const TORQUE_INTERNAL_NAMESPACE_STRING = "torque_internal"; static const char* const TORQUE_INTERNAL_NAMESPACE_STRING = "torque_internal";
static const char* const REFERENCE_TYPE_STRING = "Reference"; static const char* const REFERENCE_TYPE_STRING = "Reference";
static const char* const STRUCT_NAMESPACE_STRING = "_struct";
inline bool IsConstexprName(const std::string& name) { inline bool IsConstexprName(const std::string& name) {
return name.substr(0, std::strlen(CONSTEXPR_TYPE_PREFIX)) == return name.substr(0, std::strlen(CONSTEXPR_TYPE_PREFIX)) ==
......
...@@ -15,7 +15,7 @@ namespace internal { ...@@ -15,7 +15,7 @@ namespace internal {
namespace torque { namespace torque {
template <class Variable> template <class Variable>
V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop(); V8_EXPORT_PRIVATE typename Variable::Scope*& ContextualVariableTop();
// {ContextualVariable} provides a clean alternative to a global variable. // {ContextualVariable} provides a clean alternative to a global variable.
// The contextual variable is mutable, and supports managing the value of // The contextual variable is mutable, and supports managing the value of
...@@ -30,8 +30,6 @@ V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop(); ...@@ -30,8 +30,6 @@ V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop();
template <class Derived, class VarType> template <class Derived, class VarType>
class ContextualVariable { class ContextualVariable {
public: public:
using VariableType = VarType;
// A {Scope} contains a new object of type {VarType} and gives // A {Scope} contains a new object of type {VarType} and gives
// ContextualVariable::Get() access to it. Upon destruction, the contextual // ContextualVariable::Get() access to it. Upon destruction, the contextual
// variable is restored to the state before the {Scope} was created. Scopes // variable is restored to the state before the {Scope} was created. Scopes
...@@ -41,18 +39,20 @@ class ContextualVariable { ...@@ -41,18 +39,20 @@ class ContextualVariable {
public: public:
template <class... Args> template <class... Args>
explicit Scope(Args&&... args) explicit Scope(Args&&... args)
: current_(std::forward<Args>(args)...), previous_(Top()) { : value_(std::forward<Args>(args)...), previous_(Top()) {
Top() = &current_; Top() = this;
} }
~Scope() { ~Scope() {
// Ensure stack discipline. // Ensure stack discipline.
DCHECK_EQ(&current_, Top()); DCHECK_EQ(this, Top());
Top() = previous_; Top() = previous_;
} }
VarType& Value() { return value_; }
private: private:
VarType current_; VarType value_;
VarType* previous_; Scope* previous_;
static_assert(std::is_base_of<ContextualVariable, Derived>::value, static_assert(std::is_base_of<ContextualVariable, Derived>::value,
"Curiously Recurring Template Pattern"); "Curiously Recurring Template Pattern");
...@@ -65,13 +65,13 @@ class ContextualVariable { ...@@ -65,13 +65,13 @@ class ContextualVariable {
// for this contextual variable. // for this contextual variable.
static VarType& Get() { static VarType& Get() {
DCHECK_NOT_NULL(Top()); DCHECK_NOT_NULL(Top());
return *Top(); return Top()->Value();
} }
private: private:
template <class T> template <class T>
friend typename T::VariableType*& ContextualVariableTop(); friend typename T::Scope*& ContextualVariableTop();
static VarType*& Top() { return ContextualVariableTop<Derived>(); } static Scope*& Top() { return ContextualVariableTop<Derived>(); }
static bool HasScope() { return Top() != nullptr; } static bool HasScope() { return Top() != nullptr; }
friend class MessageBuilder; friend class MessageBuilder;
...@@ -82,11 +82,11 @@ class ContextualVariable { ...@@ -82,11 +82,11 @@ class ContextualVariable {
struct VarName \ struct VarName \
: v8::internal::torque::ContextualVariable<VarName, __VA_ARGS__> {} : v8::internal::torque::ContextualVariable<VarName, __VA_ARGS__> {}
#define DEFINE_CONTEXTUAL_VARIABLE(VarName) \ #define DEFINE_CONTEXTUAL_VARIABLE(VarName) \
template <> \ template <> \
V8_EXPORT_PRIVATE VarName::VariableType*& ContextualVariableTop<VarName>() { \ V8_EXPORT_PRIVATE VarName::Scope*& ContextualVariableTop<VarName>() { \
static thread_local VarName::VariableType* top = nullptr; \ static thread_local VarName::Scope* top = nullptr; \
return top; \ return top; \
} }
// By inheriting from {ContextualClass} a class can become a contextual variable // By inheriting from {ContextualClass} a class can become a contextual variable
......
...@@ -75,6 +75,7 @@ TypeArgumentInference Generic::InferSpecializationTypes( ...@@ -75,6 +75,7 @@ TypeArgumentInference Generic::InferSpecializationTypes(
std::vector<TypeExpression*> explicit_parameters( std::vector<TypeExpression*> explicit_parameters(
parameters.begin() + implicit_count, parameters.end()); parameters.begin() + implicit_count, parameters.end());
CurrentScope::Scope generic_scope(ParentScope());
TypeArgumentInference inference(generic_parameters(), TypeArgumentInference inference(generic_parameters(),
explicit_specialization_types, explicit_specialization_types,
explicit_parameters, arguments); explicit_parameters, arguments);
......
...@@ -20,6 +20,10 @@ TypeArgumentInference::TypeArgumentInference( ...@@ -20,6 +20,10 @@ TypeArgumentInference::TypeArgumentInference(
Fail("more explicit type arguments than expected"); Fail("more explicit type arguments than expected");
return; return;
} }
if (term_parameters.size() != term_argument_types.size()) {
Fail("number of term parameters does not match number of term arguments!");
return;
}
for (size_t i = 0; i < type_parameters.size(); i++) { for (size_t i = 0; i < type_parameters.size(); i++) {
type_parameter_from_name_[type_parameters[i]->value] = i; type_parameter_from_name_[type_parameters[i]->value] = i;
...@@ -28,7 +32,6 @@ TypeArgumentInference::TypeArgumentInference( ...@@ -28,7 +32,6 @@ TypeArgumentInference::TypeArgumentInference(
inferred_[i] = {explicit_type_arguments[i]}; inferred_[i] = {explicit_type_arguments[i]};
} }
DCHECK_EQ(term_parameters.size(), term_argument_types.size());
for (size_t i = 0; i < term_parameters.size(); i++) { for (size_t i = 0; i < term_parameters.size(); i++) {
Match(term_parameters[i], term_argument_types[i]); Match(term_parameters[i], term_argument_types[i]);
if (HasFailed()) return; if (HasFailed()) return;
......
...@@ -48,6 +48,11 @@ namespace torque { ...@@ -48,6 +48,11 @@ namespace torque {
// In the above case the inference simply ignores inconsistent constraints on // In the above case the inference simply ignores inconsistent constraints on
// `T`. Similarly, we ignore all constraints arising from formal parameters // `T`. Similarly, we ignore all constraints arising from formal parameters
// that are function- or union-typed. // that are function- or union-typed.
//
// Finally, note that term parameters are passed as type expressions, since
// we have no way of expressing a reference to type parameter as a Type. These
// type expressions are resolved during matching, so TypeArgumentInference
// should be instantiated in the appropriate scope.
class TypeArgumentInference { class TypeArgumentInference {
public: public:
TypeArgumentInference(const NameVector& type_parameters, TypeArgumentInference(const NameVector& type_parameters,
......
...@@ -39,17 +39,6 @@ const StructType* TypeOracle::GetGenericStructTypeInstance( ...@@ -39,17 +39,6 @@ const StructType* TypeOracle::GetGenericStructTypeInstance(
return *specialization; return *specialization;
} else { } else {
CurrentScope::Scope generic_scope(generic_struct->ParentScope()); CurrentScope::Scope generic_scope(generic_struct->ParentScope());
// Create a temporary fake-namespace just to temporarily declare the
// specialization aliases for the generic types to create a signature.
Namespace tmp_namespace("_tmp");
CurrentScope::Scope tmp_namespace_scope(&tmp_namespace);
auto arg_types_iterator = arg_types.begin();
for (auto param : params) {
TypeAlias* alias = Declarations::DeclareType(param, *arg_types_iterator);
alias->SetIsUserDefined(false);
arg_types_iterator++;
}
auto struct_type = TypeVisitor::ComputeType(generic_struct->declaration(), auto struct_type = TypeVisitor::ComputeType(generic_struct->declaration(),
{{generic_struct, arg_types}}); {{generic_struct, arg_types}});
specializations.Add(arg_types, struct_type); specializations.Add(arg_types, struct_type);
......
...@@ -31,11 +31,12 @@ class TypeOracle : public ContextualClass<TypeOracle> { ...@@ -31,11 +31,12 @@ class TypeOracle : public ContextualClass<TypeOracle> {
} }
static StructType* GetStructType( static StructType* GetStructType(
const std::string& basename, const StructDeclaration* decl,
StructType::MaybeSpecializationKey specialized_from) { StructType::MaybeSpecializationKey specialized_from) {
StructType* result = Namespace* nspace = new Namespace(STRUCT_NAMESPACE_STRING);
new StructType(CurrentNamespace(), basename, specialized_from); StructType* result = new StructType(nspace, decl, specialized_from);
Get().aggregate_types_.push_back(std::unique_ptr<StructType>(result)); Get().aggregate_types_.push_back(std::unique_ptr<StructType>(result));
Get().struct_namespaces_.push_back(std::unique_ptr<Namespace>(nspace));
return result; return result;
} }
...@@ -260,6 +261,7 @@ class TypeOracle : public ContextualClass<TypeOracle> { ...@@ -260,6 +261,7 @@ class TypeOracle : public ContextualClass<TypeOracle> {
std::vector<std::unique_ptr<Type>> nominal_types_; std::vector<std::unique_ptr<Type>> nominal_types_;
std::vector<std::unique_ptr<AggregateType>> aggregate_types_; std::vector<std::unique_ptr<AggregateType>> aggregate_types_;
std::vector<std::unique_ptr<Type>> top_types_; std::vector<std::unique_ptr<Type>> top_types_;
std::vector<std::unique_ptr<Namespace>> struct_namespaces_;
}; };
} // namespace torque } // namespace torque
......
...@@ -110,9 +110,20 @@ void DeclareMethods(AggregateType* container_type, ...@@ -110,9 +110,20 @@ void DeclareMethods(AggregateType* container_type,
const StructType* TypeVisitor::ComputeType( const StructType* TypeVisitor::ComputeType(
StructDeclaration* decl, StructDeclaration* decl,
StructType::MaybeSpecializationKey specialized_from) { StructType::MaybeSpecializationKey specialized_from) {
StructType* struct_type = TypeOracle::GetStructType(decl, specialized_from);
CurrentScope::Scope struct_namespace_scope(struct_type->nspace());
CurrentSourcePosition::Scope position_activator(decl->pos); CurrentSourcePosition::Scope position_activator(decl->pos);
StructType* struct_type =
TypeOracle::GetStructType(decl->name->value, specialized_from); if (specialized_from) {
auto& params = specialized_from->generic->generic_parameters();
auto arg_types_iterator = specialized_from->specialized_types.begin();
for (auto param : params) {
TypeAlias* alias = Declarations::DeclareType(param, *arg_types_iterator);
alias->SetIsUserDefined(false);
arg_types_iterator++;
}
}
size_t offset = 0; size_t offset = 0;
for (auto& field : decl->fields) { for (auto& field : decl->fields) {
CurrentSourcePosition::Scope position_activator( CurrentSourcePosition::Scope position_activator(
...@@ -132,7 +143,6 @@ const StructType* TypeVisitor::ComputeType( ...@@ -132,7 +143,6 @@ const StructType* TypeVisitor::ComputeType(
false}); false});
offset += LoweredSlotCount(field_type); offset += LoweredSlotCount(field_type);
} }
DeclareMethods(struct_type, decl->methods);
return struct_type; return struct_type;
} }
...@@ -318,6 +328,11 @@ void TypeVisitor::VisitClassFieldsAndMethods( ...@@ -318,6 +328,11 @@ void TypeVisitor::VisitClassFieldsAndMethods(
DeclareMethods(class_type, class_declaration->methods); DeclareMethods(class_type, class_declaration->methods);
} }
void TypeVisitor::VisitStructMethods(
StructType* struct_type, const StructDeclaration* struct_declaration) {
DeclareMethods(struct_type, struct_declaration->methods);
}
const StructType* TypeVisitor::ComputeTypeForStructExpression( const StructType* TypeVisitor::ComputeTypeForStructExpression(
TypeExpression* type_expression, TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types) { const std::vector<const Type*>& term_argument_types) {
...@@ -350,9 +365,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression( ...@@ -350,9 +365,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression(
term_parameters.push_back(field.name_and_type.type); term_parameters.push_back(field.name_and_type.type);
} }
CurrentScope::Scope generic_scope(generic_struct->ParentScope());
TypeArgumentInference inference( TypeArgumentInference inference(
generic_struct->declaration()->generic_parameters, generic_struct->declaration()->generic_parameters,
explicit_type_arguments, term_parameters, term_argument_types); explicit_type_arguments, term_parameters, term_argument_types);
if (inference.HasFailed()) { if (inference.HasFailed()) {
ReportError("failed to infer type arguments for struct ", basic->name, ReportError("failed to infer type arguments for struct ", basic->name,
" initialization: ", inference.GetFailureReason()); " initialization: ", inference.GetFailureReason());
......
...@@ -27,6 +27,8 @@ class TypeVisitor { ...@@ -27,6 +27,8 @@ class TypeVisitor {
static const Type* ComputeType(TypeExpression* type_expression); static const Type* ComputeType(TypeExpression* type_expression);
static void VisitClassFieldsAndMethods( static void VisitClassFieldsAndMethods(
ClassType* class_type, const ClassDeclaration* class_declaration); ClassType* class_type, const ClassDeclaration* class_declaration);
static void VisitStructMethods(StructType* struct_type,
const StructDeclaration* struct_declaration);
static Signature MakeSignature(const CallableDeclaration* declaration); static Signature MakeSignature(const CallableDeclaration* declaration);
static const StructType* ComputeTypeForStructExpression( static const StructType* ComputeTypeForStructExpression(
TypeExpression* type_expression, TypeExpression* type_expression,
......
...@@ -298,7 +298,7 @@ std::string StructType::MangledName() const { ...@@ -298,7 +298,7 @@ std::string StructType::MangledName() const {
std::stringstream result; std::stringstream result;
// TODO(gsps): Add 'ST' as a prefix once we can control the generated type // TODO(gsps): Add 'ST' as a prefix once we can control the generated type
// name from Torque code // name from Torque code
result << basename_; result << decl_->name->value;
if (specialized_from_) { if (specialized_from_) {
for (const Type* t : specialized_from_->specialized_types) { for (const Type* t : specialized_from_->specialized_types) {
std::string arg_type_string = t->MangledName(); std::string arg_type_string = t->MangledName();
...@@ -345,6 +345,17 @@ std::string StructType::ToExplicitString() const { ...@@ -345,6 +345,17 @@ std::string StructType::ToExplicitString() const {
return result.str(); return result.str();
} }
void StructType::Finalize() const {
if (is_finalized_) return;
{
CurrentScope::Scope scope_activator(nspace());
CurrentSourcePosition::Scope position_activator(decl_->pos);
TypeVisitor::VisitStructMethods(const_cast<StructType*>(this), decl_);
}
is_finalized_ = true;
CheckForDuplicateFields();
}
constexpr ClassFlags ClassType::kInternalFlags; constexpr ClassFlags ClassType::kInternalFlags;
ClassType::ClassType(const Type* parent, Namespace* nspace, ClassType::ClassType(const Type* parent, Namespace* nspace,
......
...@@ -486,22 +486,19 @@ class StructType final : public AggregateType { ...@@ -486,22 +486,19 @@ class StructType final : public AggregateType {
private: private:
friend class TypeOracle; friend class TypeOracle;
StructType(Namespace* nspace, const std::string& basename, StructType(Namespace* nspace, const StructDeclaration* decl,
MaybeSpecializationKey specialized_from = base::nullopt) MaybeSpecializationKey specialized_from = base::nullopt)
: AggregateType(Kind::kStructType, nullptr, nspace, : AggregateType(Kind::kStructType, nullptr, nspace,
ComputeName(basename, specialized_from)), ComputeName(decl->name->value, specialized_from)),
basename_(basename), decl_(decl),
specialized_from_(specialized_from) {} specialized_from_(specialized_from) {}
void Finalize() const override { void Finalize() const override;
is_finalized_ = true;
CheckForDuplicateFields();
}
static std::string ComputeName(const std::string& basename, static std::string ComputeName(const std::string& basename,
MaybeSpecializationKey specialized_from); MaybeSpecializationKey specialized_from);
std::string basename_; const StructDeclaration* decl_;
MaybeSpecializationKey specialized_from_; MaybeSpecializationKey specialized_from_;
}; };
......
...@@ -484,6 +484,20 @@ TEST(TestReferences) { ...@@ -484,6 +484,20 @@ TEST(TestReferences) {
ft.Call(); ft.Call();
} }
TEST(TestSlices) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
i::HandleScope scope(isolate);
CodeAssemblerTester asm_tester(isolate);
TestTorqueAssembler m(asm_tester.state());
{
m.TestSlices();
m.Return(m.UndefinedConstant());
}
FunctionTester ft(asm_tester.GenerateCode(), 0);
ft.Call();
}
TEST(TestStaticAssert) { TEST(TestStaticAssert) {
CcTest::InitializeVM(); CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate()); Isolate* isolate(CcTest::i_isolate());
......
...@@ -842,6 +842,31 @@ namespace test { ...@@ -842,6 +842,31 @@ namespace test {
check(array.b == 9); check(array.b == 9);
} }
@export
macro TestSlices() {
const fixedArray: FixedArray = AllocateZeroedFixedArray(3);
// TODO(gsps): Directly reference fixedArray.objects once supported
let slice = torque_internal::UnsafeNewSlice<Smi>(
fixedArray,
(& fixedArray.length).offset + torque_internal::SizeOf<Smi>(), 3);
for (let i: intptr = 0; i < slice.length; i++) {
let ref = slice.Access(i) otherwise unreachable;
check(* ref == 0);
* ref = Convert<Smi>(i) + 7;
}
let it = slice.Iterator();
let count: Smi = 0;
while (true) {
let ref = it.Next() otherwise break;
check(* ref == count + 7);
count++;
}
check(count == 3);
check(it.Empty());
}
@export @export
macro TestStaticAssert() { macro TestStaticAssert() {
StaticAssert(1 + 2 == 3); StaticAssert(1 + 2 == 3);
......
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