[torque]: Add the ability to test Torque functionality with cctest

In the process, add a few simple tests for "constexpr" expressions, which
identified a few bugs that are also fixed in this CL.

Change-Id: I97486c781572642d2b574b92133b1f9cda3db592
Reviewed-on: https://chromium-review.googlesource.com/1055493
Commit-Queue: Daniel Clifford <danno@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53135}
This commit is contained in:
Daniel Clifford 2018-05-13 12:10:44 +02:00 committed by Commit Bot
parent 3fc8937ed1
commit 5f920f770d
14 changed files with 248 additions and 25 deletions

View File

@ -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

View File

@ -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;

View File

@ -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_

View File

@ -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:

View File

@ -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

View File

@ -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_;
}

View File

@ -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) {

View File

@ -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 "

View File

@ -78,6 +78,8 @@ class Declarations {
return source_file_map_->PositionAsString(pos);
}
void PrintScopeChain() { chain_.Print(); }
class NodeScopeActivator;
private:

View File

@ -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;

View File

@ -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

View File

@ -1,3 +1,4 @@
include_rules = [
"+src",
"+torque-generated"
]

View File

@ -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 <cmath>
#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

View File

@ -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<bool>(IsFastElementsKind(PACKED_SMI_ELEMENTS)));
}
macro TestConstexprIf() {
assert(ElementsKindTestHelper1(UINT8_ELEMENTS));
assert(ElementsKindTestHelper1(UINT16_ELEMENTS));
assert(!ElementsKindTestHelper1(UINT32_ELEMENTS));
}
macro TestConstexprReturn() {
assert(convert<bool>(ElementsKindTestHelper3(UINT8_ELEMENTS)));
assert(convert<bool>(ElementsKindTestHelper3(UINT16_ELEMENTS)));
assert(!convert<bool>(ElementsKindTestHelper3(UINT32_ELEMENTS)));
assert(convert<bool>(!ElementsKindTestHelper3(UINT32_ELEMENTS)));
}
}