diff --git a/BUILD.gn b/BUILD.gn index 1b5fe506e3..e6406c3b0c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -846,18 +846,21 @@ torque_files = [ "src/builtins/base.tq", "src/builtins/array.tq", "src/builtins/typed-array.tq", + "test/torque/test-torque.tq", ] torque_modules = [ "base", "array", "typed-array", + "test", ] action("run_torque") { visibility = [ ":*", "tools/gcmole/:*", + "test/cctest/:*", ] # We reuse the snapshot toolchain for building torque to not build v8_libbase diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 1f439d3bfa..ddae35e970 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.tq @@ -136,6 +136,10 @@ extern operator '<=' macro SmiLessThanOrEqual(Smi, Smi): bool; extern operator '>' macro SmiGreaterThan(Smi, Smi): bool; extern operator '>=' macro SmiGreaterThanOrEqual(Smi, Smi): bool; +extern operator '==' macro ElementsKindEqual( + constexpr ElementsKind, constexpr ElementsKind): constexpr bool; +extern macro IsFastElementsKind(constexpr ElementsKind): constexpr bool; + extern macro SmiAbove(Smi, Smi): bool; extern operator '==' macro WordEqual(intptr, intptr): bool; @@ -175,6 +179,7 @@ extern operator '-' macro NumberSub(Number, Number): Number; extern operator 'min' macro NumberMin(Number, Number): Number; extern operator 'max' macro NumberMax(Number, Number): Number; +extern operator '!' macro ConstexprBoolNot(constexpr bool): constexpr bool; extern operator '!' macro Word32BinaryNot(bool): bool; extern operator '.map' macro LoadMap(HeapObject): Map; diff --git a/src/builtins/builtins-test-gen.h b/src/builtins/builtins-test-gen.h new file mode 100644 index 0000000000..5412beb9bd --- /dev/null +++ b/src/builtins/builtins-test-gen.h @@ -0,0 +1,22 @@ +// Copyright 2018 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. + +#ifndef V8_BUILTINS_BUILTINS_TEST_GEN_H_ +#define V8_BUILTINS_BUILTINS_TEST_GEN_H_ + +#include "torque-generated/builtins-base-from-dsl-gen.h" + +namespace v8 { +namespace internal { + +class TestBuiltinsAssembler : public BaseBuiltinsFromDSLAssembler { + public: + explicit TestBuiltinsAssembler(compiler::CodeAssemblerState* state) + : BaseBuiltinsFromDSLAssembler(state) {} +}; + +} // namespace internal +} // namespace v8 + +#endif // V8_BUILTINS_BUILTINS_TEST_GEN_H_ diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 20052f04f3..8fa6c0286e 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -1489,6 +1489,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { // ElementsKind helpers: Node* IsFastElementsKind(Node* elements_kind); + bool IsFastElementsKind(ElementsKind kind) { + return v8::internal::IsFastElementsKind(kind); + } Node* IsFastSmiOrTaggedElementsKind(Node* elements_kind); Node* IsFastSmiElementsKind(Node* elements_kind); Node* IsHoleyFastElementsKind(Node* elements_kind); @@ -2337,6 +2340,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { Unreachable(); } + bool ConstexprBoolNot(bool value) { return !value; } + void PerformStackCheck(Node* context); protected: diff --git a/src/elements-kind.h b/src/elements-kind.h index b00966ef10..13a824940f 100644 --- a/src/elements-kind.h +++ b/src/elements-kind.h @@ -264,6 +264,7 @@ inline bool IsTransitionableFastElementsKind(ElementsKind from_kind) { from_kind != TERMINAL_FAST_ELEMENTS_KIND; } +inline bool ElementsKindEqual(ElementsKind a, ElementsKind b) { return a == b; } } // namespace internal } // namespace v8 diff --git a/src/torque/declarable.h b/src/torque/declarable.h index b595d96c06..7aa9177943 100644 --- a/src/torque/declarable.h +++ b/src/torque/declarable.h @@ -79,6 +79,7 @@ class Type : public Declarable { bool IsVoid() const { return name() == VOID_TYPE_STRING; } bool IsNever() const { return name() == NEVER_TYPE_STRING; } bool IsBool() const { return name() == BOOL_TYPE_STRING; } + bool IsConstexprBool() const { return name() == CONSTEXPR_BOOL_TYPE_STRING; } bool IsVoidOrNever() const { return IsVoid() || IsNever(); } bool IsConstexpr() const { return name().substr(0, strlen(CONSTEXPR_TYPE_PREFIX)) == @@ -136,7 +137,13 @@ class Variable : public Value { DECLARE_DECLARABLE_BOILERPLATE(Variable, variable); bool IsConst() const override { return false; } std::string GetValueForDeclaration() const override { return value_; } - std::string GetValueForRead() const override { return value_ + "->value()"; } + std::string GetValueForRead() const override { + if (type()->IsConstexpr()) { + return std::string("*") + value_; + } else { + return value_ + "->value()"; + } + } std::string GetValueForWrite() const override { return std::string("*") + value_; } diff --git a/src/torque/declaration-visitor.cc b/src/torque/declaration-visitor.cc index 9efdc9515f..536a716aea 100644 --- a/src/torque/declaration-visitor.cc +++ b/src/torque/declaration-visitor.cc @@ -129,6 +129,9 @@ void DeclarationVisitor::Visit(ReturnStatement* stmt) { MarkVariableModified(Variable::cast( declarations()->LookupValue(stmt->pos, kReturnValueVariable))); } + if (stmt->value) { + Visit(*stmt->value); + } } void DeclarationVisitor::Visit(ForOfLoopStatement* stmt) { diff --git a/src/torque/declaration-visitor.h b/src/torque/declaration-visitor.h index 91faa0b722..46c5408709 100644 --- a/src/torque/declaration-visitor.h +++ b/src/torque/declaration-visitor.h @@ -208,6 +208,12 @@ class DeclarationVisitor : public FileVisitor { void Visit(VarDeclarationStatement* stmt) { std::string variable_name = stmt->name; const Type* type = declarations()->LookupType(stmt->pos, stmt->type); + if (type->IsConstexpr()) { + std::stringstream stream; + stream << "cannot declare variable with constexpr type at " + << PositionAsString(stmt->pos); + ReportError(stream.str()); + } declarations()->DeclareVariable(stmt->pos, variable_name, type); if (global_context_.verbose()) { std::cout << "declared variable " << variable_name << " with type " diff --git a/src/torque/declarations.h b/src/torque/declarations.h index 9d176268f0..9ec73ba752 100644 --- a/src/torque/declarations.h +++ b/src/torque/declarations.h @@ -78,6 +78,8 @@ class Declarations { return source_file_map_->PositionAsString(pos); } + void PrintScopeChain() { chain_.Print(); } + class NodeScopeActivator; private: diff --git a/src/torque/implementation-visitor.cc b/src/torque/implementation-visitor.cc index 05cad9bbfb..f63f09fa39 100644 --- a/src/torque/implementation-visitor.cc +++ b/src/torque/implementation-visitor.cc @@ -65,6 +65,7 @@ void ImplementationVisitor::Visit(ModuleDeclaration* decl) { source << "#include \"src/builtins/builtins-utils-gen.h\"" << std::endl; source << "#include \"src/builtins/builtins.h\"" << std::endl; source << "#include \"src/code-factory.h\"" << std::endl; + source << "#include \"src/elements-kind.h\"" << std::endl; source << "#include \"src/heap/factory-inl.h\"" << std::endl; source << "#include \"src/objects.h\"" << std::endl; @@ -149,13 +150,17 @@ void ImplementationVisitor::Visit(MacroDeclaration* decl) { const Variable* result_var = nullptr; if (macro->HasReturnValue()) { - GenerateIndent(); - source_out() << "Node* return_default = &*SmiConstant(0);" << std::endl; const Type* return_type = macro->signature().return_type; - VisitResult init = {return_type, - std::string("UncheckedCast<") + - return_type->GetGeneratedTNodeTypeName() + - ">(return_default)"}; + if (!return_type->IsConstexpr()) { + GenerateIndent(); + source_out() << "Node* return_default = &*SmiConstant(0);" << std::endl; + } + VisitResult init = { + return_type, + return_type->IsConstexpr() + ? (return_type->GetGeneratedTypeName() + "()") + : (std::string("UncheckedCast<") + + return_type->GetGeneratedTNodeTypeName() + ">(return_default)")}; result_var = GenerateVariableDeclaration(decl, kReturnValueVariable, {}, init); } @@ -335,42 +340,73 @@ VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) { } VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) { + VisitResult left_result; { Declarations::NodeScopeActivator scope(declarations(), expr->left); Label* false_label = declarations()->LookupLabel(expr->pos, kFalseLabelName); GenerateLabelDefinition(false_label); - VisitResult left_result = Visit(expr->left); + left_result = Visit(expr->left); if (left_result.type()->IsBool()) { Label* true_label = declarations()->LookupLabel(expr->pos, kTrueLabelName); GenerateIndent(); source_out() << "GotoIf(" << left_result.variable() << ", " << true_label->generated() << ");" << std::endl; - } else { + } else if (!left_result.type()->IsConstexprBool()) { GenerateLabelBind(false_label); } } - return Visit(expr->right); + VisitResult right_result = Visit(expr->right); + if (right_result.type() != left_result.type()) { + std::stringstream stream; + stream << "types of left and right expression of logical OR don't match (\"" + << left_result.type() << "\" vs. \"" << right_result.type() + << "\") at " << PositionAsString(expr->pos); + ReportError(stream.str()); + } + if (left_result.type()->IsConstexprBool()) { + return VisitResult(left_result.type(), std::string("(") + + left_result.variable() + " || " + + right_result.variable() + ")"); + } else { + return right_result; + } } VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) { + VisitResult left_result; { Declarations::NodeScopeActivator scope(declarations(), expr->left); Label* true_label = declarations()->LookupLabel(expr->pos, kTrueLabelName); GenerateLabelDefinition(true_label); - VisitResult left_result = Visit(expr->left); + left_result = Visit(expr->left); if (left_result.type()->IsBool()) { Label* false_label = declarations()->LookupLabel(expr->pos, kFalseLabelName); GenerateIndent(); source_out() << "GotoIfNot(" << left_result.variable() << ", " << false_label->generated() << ");" << std::endl; - } else { + } else if (!left_result.type()->IsConstexprBool()) { GenerateLabelBind(true_label); } } - return Visit(expr->right); + VisitResult right_result = Visit(expr->right); + if (right_result.type() != left_result.type()) { + std::stringstream stream; + stream + << "types of left and right expression of logical AND don't match (\"" + << left_result.type() << "\" vs. \"" << right_result.type() << "\") at " + << PositionAsString(expr->pos); + ReportError(stream.str()); + } + if (left_result.type()->IsConstexprBool()) { + return VisitResult(left_result.type(), std::string("(") + + left_result.variable() + " && " + + right_result.variable() + ")"); + } else { + return right_result; + } } VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) { @@ -494,24 +530,33 @@ const Type* ImplementationVisitor::Visit(IfStatement* stmt) { ReportError(stream.str()); } + const Type* left_result; + const Type* right_result = GetTypeOracle().GetVoidType(); { GenerateIndent(); source_out() << "if ((" << expression_result.variable() << ")) "; ScopedIndent indent(this, false); source_out() << std::endl; - Visit(stmt->if_true); + left_result = Visit(stmt->if_true); } if (has_else) { source_out() << " else "; ScopedIndent indent(this, false); source_out() << std::endl; - Visit(*stmt->if_false); + right_result = Visit(*stmt->if_false); + } + if (left_result->IsNever() != right_result->IsNever()) { + std::stringstream stream; + stream << "either both or neither branches in a constexpr if statement " + "must reach their end at" + << PositionAsString(stmt->pos); + ReportError(stream.str()); } source_out() << std::endl; - return GetTypeOracle().GetVoidType(); + return left_result; } else { Label* true_label = nullptr; Label* false_label = nullptr; @@ -1041,6 +1086,7 @@ void ImplementationVisitor::GenerateChangedVarsFromControlSplit(AstNode* node) { source_out() << "{"; bool first = true; for (auto v : changed_vars) { + if (v->type()->IsConstexpr()) continue; if (first) { first = false; } else { @@ -1164,10 +1210,16 @@ Variable* ImplementationVisitor::GenerateVariableDeclaration( } GenerateIndent(); - source_out() << "TVARIABLE("; - source_out() << variable->type()->GetGeneratedTNodeTypeName(); - source_out() << ", " << variable->GetValueForDeclaration() << "_impl);" - << std::endl; + if (variable->type()->IsConstexpr()) { + source_out() << variable->type()->GetGeneratedTypeName(); + source_out() << " " << variable->GetValueForDeclaration() << "_impl;" + << std::endl; + } else { + source_out() << "TVARIABLE("; + source_out() << variable->type()->GetGeneratedTNodeTypeName(); + source_out() << ", " << variable->GetValueForDeclaration() << "_impl);" + << std::endl; + } GenerateIndent(); source_out() << "auto " << variable->GetValueForDeclaration() << " = &" << variable->GetValueForDeclaration() << "_impl;" << std::endl; @@ -1228,9 +1280,11 @@ VisitResult ImplementationVisitor::GenerateCall( GenerateIndent(); } else { result_variable_name = GenerateNewTempVariable(result_type); - source_out() << "UncheckedCast<"; - source_out() << result_type->GetGeneratedTNodeTypeName(); - source_out() << ">("; + if (!result_type->IsConstexpr()) { + source_out() << "UncheckedCast<"; + source_out() << result_type->GetGeneratedTNodeTypeName(); + source_out() << ">("; + } } if (callable->IsBuiltin()) { if (is_tailcall) { @@ -1313,7 +1367,8 @@ VisitResult ImplementationVisitor::GenerateCall( std::cout << "finished generating code for call to " << callable_name << " at " << PositionAsString(pos) << "" << std::endl; } - if (!result_type->IsVoidOrNever() && !is_tailcall) { + if (!result_type->IsVoidOrNever() && !is_tailcall && + !result_type->IsConstexpr()) { source_out() << ")"; } source_out() << ");" << std::endl; diff --git a/test/cctest/BUILD.gn b/test/cctest/BUILD.gn index 1aa4a07ca4..273cc1129d 100644 --- a/test/cctest/BUILD.gn +++ b/test/cctest/BUILD.gn @@ -229,6 +229,7 @@ v8_source_set("cctest_sources") { "test-version.cc", "test-weakmaps.cc", "test-weaksets.cc", + "torque/test-torque.cc", "trace-extension.cc", "trace-extension.h", "types-fuzz.h", @@ -381,7 +382,9 @@ v8_source_set("cctest_sources") { ] defines = [] - deps = [] + deps = [ + "../..:run_torque", + ] if (is_component_build) { # cctest can't be built against a shared library, so we diff --git a/test/cctest/DEPS b/test/cctest/DEPS index 3e73aa244f..7210fb5317 100644 --- a/test/cctest/DEPS +++ b/test/cctest/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+src", + "+torque-generated" ] diff --git a/test/cctest/torque/test-torque.cc b/test/cctest/torque/test-torque.cc new file mode 100644 index 0000000000..f7bbb8ba58 --- /dev/null +++ b/test/cctest/torque/test-torque.cc @@ -0,0 +1,70 @@ +// Copyright 2015 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 + +#include "src/api.h" +#include "src/base/utils/random-number-generator.h" +#include "src/builtins/builtins-promise-gen.h" +#include "src/builtins/builtins-string-gen.h" +#include "src/char-predicates.h" +#include "src/code-factory.h" +#include "src/code-stub-assembler.h" +#include "src/compiler/node.h" +#include "src/debug/debug.h" +#include "src/elements-kind.h" +#include "src/isolate.h" +#include "src/objects-inl.h" +#include "src/objects/promise-inl.h" +#include "test/cctest/compiler/code-assembler-tester.h" +#include "test/cctest/compiler/function-tester.h" +#include "torque-generated/builtins-test-from-dsl-gen.h" + +namespace v8 { +namespace internal { +namespace compiler { + +namespace { + +typedef CodeAssemblerLabel Label; +typedef CodeAssemblerVariable Variable; + +} // namespace + +TEST(TestConstexpr1) { + Isolate* isolate(CcTest::InitIsolateOnce()); + CodeAssemblerTester asm_tester(isolate, 0); + TestBuiltinsFromDSLAssembler m(asm_tester.state()); + { + m.TestConstexpr1(); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), 0); +} + +TEST(TestConstexprIf) { + Isolate* isolate(CcTest::InitIsolateOnce()); + CodeAssemblerTester asm_tester(isolate, 0); + TestBuiltinsFromDSLAssembler m(asm_tester.state()); + { + m.TestConstexprIf(); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), 0); +} + +TEST(TestConstexprReturn) { + Isolate* isolate(CcTest::InitIsolateOnce()); + CodeAssemblerTester asm_tester(isolate, 0); + TestBuiltinsFromDSLAssembler m(asm_tester.state()); + { + m.TestConstexprReturn(); + m.Return(m.UndefinedConstant()); + } + FunctionTester ft(asm_tester.GenerateCode(), 0); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/test/torque/test-torque.tq b/test/torque/test-torque.tq new file mode 100644 index 0000000000..388c4de95f --- /dev/null +++ b/test/torque/test-torque.tq @@ -0,0 +1,40 @@ +// Copyright 2018 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. + +module test { + +macro ElementsKindTestHelper1(kind: constexpr ElementsKind): bool { + if constexpr ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS)) { + return true; + } else { + return false; + } +} + +macro ElementsKindTestHelper2(kind: constexpr ElementsKind): bool { + return ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS)); +} + +macro ElementsKindTestHelper3(kind: constexpr ElementsKind): constexpr bool { + return ((kind == UINT8_ELEMENTS) || (kind == UINT16_ELEMENTS)); +} + +macro TestConstexpr1() { + assert(convert(IsFastElementsKind(PACKED_SMI_ELEMENTS))); +} + +macro TestConstexprIf() { + assert(ElementsKindTestHelper1(UINT8_ELEMENTS)); + assert(ElementsKindTestHelper1(UINT16_ELEMENTS)); + assert(!ElementsKindTestHelper1(UINT32_ELEMENTS)); +} + +macro TestConstexprReturn() { + assert(convert(ElementsKindTestHelper3(UINT8_ELEMENTS))); + assert(convert(ElementsKindTestHelper3(UINT16_ELEMENTS))); + assert(!convert(ElementsKindTestHelper3(UINT32_ELEMENTS))); + assert(convert(!ElementsKindTestHelper3(UINT32_ELEMENTS))); +} + +}