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 {
extern class WasmExceptionTag extends Struct { index: Smi; }
const kTaggedSize: constexpr int31 generates 'kTaggedSize';
const kSmiTagSize: constexpr int31 generates 'kSmiTagSize';
const V8_INFINITY: constexpr float64 generates 'V8_INFINITY';
......
......@@ -3,10 +3,87 @@
// found in the LICENSE file.
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> {
const object: HeapObject;
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
......@@ -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 TORQUE_INTERNAL_NAMESPACE_STRING = "torque_internal";
static const char* const REFERENCE_TYPE_STRING = "Reference";
static const char* const STRUCT_NAMESPACE_STRING = "_struct";
inline bool IsConstexprName(const std::string& name) {
return name.substr(0, std::strlen(CONSTEXPR_TYPE_PREFIX)) ==
......
......@@ -15,7 +15,7 @@ namespace internal {
namespace torque {
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.
// The contextual variable is mutable, and supports managing the value of
......@@ -30,8 +30,6 @@ V8_EXPORT_PRIVATE typename Variable::VariableType*& ContextualVariableTop();
template <class Derived, class VarType>
class ContextualVariable {
public:
using VariableType = VarType;
// A {Scope} contains a new object of type {VarType} and gives
// ContextualVariable::Get() access to it. Upon destruction, the contextual
// variable is restored to the state before the {Scope} was created. Scopes
......@@ -41,18 +39,20 @@ class ContextualVariable {
public:
template <class... Args>
explicit Scope(Args&&... args)
: current_(std::forward<Args>(args)...), previous_(Top()) {
Top() = &current_;
: value_(std::forward<Args>(args)...), previous_(Top()) {
Top() = this;
}
~Scope() {
// Ensure stack discipline.
DCHECK_EQ(&current_, Top());
DCHECK_EQ(this, Top());
Top() = previous_;
}
VarType& Value() { return value_; }
private:
VarType current_;
VarType* previous_;
VarType value_;
Scope* previous_;
static_assert(std::is_base_of<ContextualVariable, Derived>::value,
"Curiously Recurring Template Pattern");
......@@ -65,13 +65,13 @@ class ContextualVariable {
// for this contextual variable.
static VarType& Get() {
DCHECK_NOT_NULL(Top());
return *Top();
return Top()->Value();
}
private:
template <class T>
friend typename T::VariableType*& ContextualVariableTop();
static VarType*& Top() { return ContextualVariableTop<Derived>(); }
friend typename T::Scope*& ContextualVariableTop();
static Scope*& Top() { return ContextualVariableTop<Derived>(); }
static bool HasScope() { return Top() != nullptr; }
friend class MessageBuilder;
......@@ -84,8 +84,8 @@ class ContextualVariable {
#define DEFINE_CONTEXTUAL_VARIABLE(VarName) \
template <> \
V8_EXPORT_PRIVATE VarName::VariableType*& ContextualVariableTop<VarName>() { \
static thread_local VarName::VariableType* top = nullptr; \
V8_EXPORT_PRIVATE VarName::Scope*& ContextualVariableTop<VarName>() { \
static thread_local VarName::Scope* top = nullptr; \
return top; \
}
......
......@@ -75,6 +75,7 @@ TypeArgumentInference Generic::InferSpecializationTypes(
std::vector<TypeExpression*> explicit_parameters(
parameters.begin() + implicit_count, parameters.end());
CurrentScope::Scope generic_scope(ParentScope());
TypeArgumentInference inference(generic_parameters(),
explicit_specialization_types,
explicit_parameters, arguments);
......
......@@ -20,6 +20,10 @@ TypeArgumentInference::TypeArgumentInference(
Fail("more explicit type arguments than expected");
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++) {
type_parameter_from_name_[type_parameters[i]->value] = i;
......@@ -28,7 +32,6 @@ TypeArgumentInference::TypeArgumentInference(
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++) {
Match(term_parameters[i], term_argument_types[i]);
if (HasFailed()) return;
......
......@@ -48,6 +48,11 @@ namespace torque {
// In the above case the inference simply ignores inconsistent constraints on
// `T`. Similarly, we ignore all constraints arising from formal parameters
// 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 {
public:
TypeArgumentInference(const NameVector& type_parameters,
......
......@@ -39,17 +39,6 @@ const StructType* TypeOracle::GetGenericStructTypeInstance(
return *specialization;
} else {
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(),
{{generic_struct, arg_types}});
specializations.Add(arg_types, struct_type);
......
......@@ -31,11 +31,12 @@ class TypeOracle : public ContextualClass<TypeOracle> {
}
static StructType* GetStructType(
const std::string& basename,
const StructDeclaration* decl,
StructType::MaybeSpecializationKey specialized_from) {
StructType* result =
new StructType(CurrentNamespace(), basename, specialized_from);
Namespace* nspace = new Namespace(STRUCT_NAMESPACE_STRING);
StructType* result = new StructType(nspace, decl, specialized_from);
Get().aggregate_types_.push_back(std::unique_ptr<StructType>(result));
Get().struct_namespaces_.push_back(std::unique_ptr<Namespace>(nspace));
return result;
}
......@@ -260,6 +261,7 @@ class TypeOracle : public ContextualClass<TypeOracle> {
std::vector<std::unique_ptr<Type>> nominal_types_;
std::vector<std::unique_ptr<AggregateType>> aggregate_types_;
std::vector<std::unique_ptr<Type>> top_types_;
std::vector<std::unique_ptr<Namespace>> struct_namespaces_;
};
} // namespace torque
......
......@@ -110,9 +110,20 @@ void DeclareMethods(AggregateType* container_type,
const StructType* TypeVisitor::ComputeType(
StructDeclaration* decl,
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);
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;
for (auto& field : decl->fields) {
CurrentSourcePosition::Scope position_activator(
......@@ -132,7 +143,6 @@ const StructType* TypeVisitor::ComputeType(
false});
offset += LoweredSlotCount(field_type);
}
DeclareMethods(struct_type, decl->methods);
return struct_type;
}
......@@ -318,6 +328,11 @@ void TypeVisitor::VisitClassFieldsAndMethods(
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(
TypeExpression* type_expression,
const std::vector<const Type*>& term_argument_types) {
......@@ -350,9 +365,11 @@ const StructType* TypeVisitor::ComputeTypeForStructExpression(
term_parameters.push_back(field.name_and_type.type);
}
CurrentScope::Scope generic_scope(generic_struct->ParentScope());
TypeArgumentInference inference(
generic_struct->declaration()->generic_parameters,
explicit_type_arguments, term_parameters, term_argument_types);
if (inference.HasFailed()) {
ReportError("failed to infer type arguments for struct ", basic->name,
" initialization: ", inference.GetFailureReason());
......
......@@ -27,6 +27,8 @@ class TypeVisitor {
static const Type* ComputeType(TypeExpression* type_expression);
static void VisitClassFieldsAndMethods(
ClassType* class_type, const ClassDeclaration* class_declaration);
static void VisitStructMethods(StructType* struct_type,
const StructDeclaration* struct_declaration);
static Signature MakeSignature(const CallableDeclaration* declaration);
static const StructType* ComputeTypeForStructExpression(
TypeExpression* type_expression,
......
......@@ -298,7 +298,7 @@ std::string StructType::MangledName() const {
std::stringstream result;
// TODO(gsps): Add 'ST' as a prefix once we can control the generated type
// name from Torque code
result << basename_;
result << decl_->name->value;
if (specialized_from_) {
for (const Type* t : specialized_from_->specialized_types) {
std::string arg_type_string = t->MangledName();
......@@ -345,6 +345,17 @@ std::string StructType::ToExplicitString() const {
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;
ClassType::ClassType(const Type* parent, Namespace* nspace,
......
......@@ -486,22 +486,19 @@ class StructType final : public AggregateType {
private:
friend class TypeOracle;
StructType(Namespace* nspace, const std::string& basename,
StructType(Namespace* nspace, const StructDeclaration* decl,
MaybeSpecializationKey specialized_from = base::nullopt)
: AggregateType(Kind::kStructType, nullptr, nspace,
ComputeName(basename, specialized_from)),
basename_(basename),
ComputeName(decl->name->value, specialized_from)),
decl_(decl),
specialized_from_(specialized_from) {}
void Finalize() const override {
is_finalized_ = true;
CheckForDuplicateFields();
}
void Finalize() const override;
static std::string ComputeName(const std::string& basename,
MaybeSpecializationKey specialized_from);
std::string basename_;
const StructDeclaration* decl_;
MaybeSpecializationKey specialized_from_;
};
......
......@@ -484,6 +484,20 @@ TEST(TestReferences) {
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) {
CcTest::InitializeVM();
Isolate* isolate(CcTest::i_isolate());
......
......@@ -842,6 +842,31 @@ namespace test {
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
macro TestStaticAssert() {
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