[asm-wasm] Fix continue target of do-while loops
See associated bug: A continue if a do-while loop did jump back to the loop header, instead of jumping to the condition. This CL fixes this and adds a test case. R=bradnelson@chromium.org, titzer@chromium.org BUG=v8:5912 Review-Url: https://codereview.chromium.org/2693993002 Cr-Commit-Position: refs/heads/master@{#43178}
This commit is contained in:
parent
db558210d8
commit
0b287bd4ea
@ -36,6 +36,8 @@ namespace wasm {
|
||||
if (HasStackOverflow()) return; \
|
||||
} while (false)
|
||||
|
||||
namespace {
|
||||
|
||||
enum AsmScope { kModuleScope, kInitScope, kFuncScope, kExportScope };
|
||||
enum ValueFate { kDrop, kLeaveOnStack };
|
||||
|
||||
@ -45,6 +47,10 @@ struct ForeignVariable {
|
||||
ValueType type;
|
||||
};
|
||||
|
||||
enum TargetType : uint8_t { NoTarget, BreakTarget, ContinueTarget };
|
||||
|
||||
} // namespace
|
||||
|
||||
class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
public:
|
||||
AsmWasmBuilderImpl(Isolate* isolate, Zone* zone, CompilationInfo* info,
|
||||
@ -231,7 +237,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
}
|
||||
if (scope_ == kFuncScope) {
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
BreakTarget);
|
||||
RECURSE(VisitStatements(stmt->statements()));
|
||||
} else {
|
||||
RECURSE(VisitStatements(stmt->statements()));
|
||||
@ -244,10 +251,9 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
public:
|
||||
BlockVisitor(AsmWasmBuilderImpl* builder, BreakableStatement* stmt,
|
||||
WasmOpcode opcode)
|
||||
WasmOpcode opcode, TargetType target_type = NoTarget)
|
||||
: builder_(builder) {
|
||||
builder_->breakable_blocks_.push_back(
|
||||
std::make_pair(stmt, opcode == kExprLoop));
|
||||
builder_->breakable_blocks_.emplace_back(stmt, target_type);
|
||||
// block and loops have a type immediate.
|
||||
builder_->current_function_builder_->EmitWithU8(opcode, kLocalVoid);
|
||||
}
|
||||
@ -295,9 +301,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitIfStatement(IfStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
RECURSE(Visit(stmt->condition()));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
// WASM ifs come with implement blocks for both arms.
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
// Wasm ifs come with implicit blocks for both arms.
|
||||
BlockVisitor block(this, nullptr, kExprIf);
|
||||
if (stmt->HasThenStatement()) {
|
||||
RECURSE(Visit(stmt->then_statement()));
|
||||
}
|
||||
@ -305,18 +310,15 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
current_function_builder_->Emit(kExprElse);
|
||||
RECURSE(Visit(stmt->else_statement()));
|
||||
}
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
breakable_blocks_.pop_back();
|
||||
}
|
||||
|
||||
void DoBreakOrContinue(BreakableStatement* target, bool is_continue) {
|
||||
void DoBreakOrContinue(BreakableStatement* target, TargetType type) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
for (int i = static_cast<int>(breakable_blocks_.size()) - 1; i >= 0; --i) {
|
||||
auto elem = breakable_blocks_.at(i);
|
||||
if (elem.first == target && elem.second == is_continue) {
|
||||
if (elem.first == target && elem.second == type) {
|
||||
int block_distance = static_cast<int>(breakable_blocks_.size() - i - 1);
|
||||
current_function_builder_->Emit(kExprBr);
|
||||
current_function_builder_->EmitVarInt(block_distance);
|
||||
current_function_builder_->EmitWithVarInt(kExprBr, block_distance);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -324,11 +326,11 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
}
|
||||
|
||||
void VisitContinueStatement(ContinueStatement* stmt) {
|
||||
DoBreakOrContinue(stmt->target(), true);
|
||||
DoBreakOrContinue(stmt->target(), ContinueTarget);
|
||||
}
|
||||
|
||||
void VisitBreakStatement(BreakStatement* stmt) {
|
||||
DoBreakOrContinue(stmt->target(), false);
|
||||
DoBreakOrContinue(stmt->target(), BreakTarget);
|
||||
}
|
||||
|
||||
void VisitReturnStatement(ReturnStatement* stmt) {
|
||||
@ -366,7 +368,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
current_function_builder_->Emit(kExprI32LtS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
if_depth++;
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
breakable_blocks_.emplace_back(nullptr, NoTarget);
|
||||
HandleCase(node->left, case_to_block, tag, default_block, if_depth);
|
||||
current_function_builder_->Emit(kExprElse);
|
||||
}
|
||||
@ -376,7 +378,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
current_function_builder_->Emit(kExprI32GtS);
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
if_depth++;
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
breakable_blocks_.emplace_back(nullptr, NoTarget);
|
||||
HandleCase(node->right, case_to_block, tag, default_block, if_depth);
|
||||
current_function_builder_->Emit(kExprElse);
|
||||
}
|
||||
@ -430,7 +432,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (case_count == 0) {
|
||||
return;
|
||||
}
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor visitor(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
BreakTarget);
|
||||
ZoneVector<BlockVisitor*> blocks(zone_);
|
||||
ZoneVector<int32_t> cases(zone_);
|
||||
ZoneMap<int, unsigned int> case_to_block(zone_);
|
||||
@ -476,26 +479,28 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
|
||||
void VisitDoWhileStatement(DoWhileStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
BreakTarget);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
RECURSE(Visit(stmt->body()));
|
||||
{
|
||||
BlockVisitor inner_block(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
ContinueTarget);
|
||||
RECURSE(Visit(stmt->body()));
|
||||
}
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
current_function_builder_->EmitWithU8(kExprBr, 1);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
current_function_builder_->EmitWithU8(kExprBrIf, 0);
|
||||
}
|
||||
|
||||
void VisitWhileStatement(WhileStatement* stmt) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
BreakTarget);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop,
|
||||
ContinueTarget);
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
current_function_builder_->EmitWithU8(kExprIf, kLocalVoid);
|
||||
BlockVisitor if_block(this, nullptr, kExprIf);
|
||||
RECURSE(Visit(stmt->body()));
|
||||
current_function_builder_->EmitWithU8(kExprBr, 1);
|
||||
current_function_builder_->Emit(kExprEnd);
|
||||
breakable_blocks_.pop_back();
|
||||
}
|
||||
|
||||
void VisitForStatement(ForStatement* stmt) {
|
||||
@ -503,8 +508,10 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
if (stmt->init() != nullptr) {
|
||||
RECURSE(Visit(stmt->init()));
|
||||
}
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop);
|
||||
BlockVisitor block(this, stmt->AsBreakableStatement(), kExprBlock,
|
||||
BreakTarget);
|
||||
BlockVisitor loop(this, stmt->AsBreakableStatement(), kExprLoop,
|
||||
ContinueTarget);
|
||||
if (stmt->cond() != nullptr) {
|
||||
RECURSE(Visit(stmt->cond()));
|
||||
current_function_builder_->Emit(kExprI32Eqz);
|
||||
@ -562,8 +569,8 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
void VisitConditional(Conditional* expr) {
|
||||
DCHECK_EQ(kFuncScope, scope_);
|
||||
RECURSE(Visit(expr->condition()));
|
||||
// WASM ifs come with implicit blocks for both arms.
|
||||
breakable_blocks_.push_back(std::make_pair(nullptr, false));
|
||||
// Wasm ifs come with implicit blocks for both arms.
|
||||
breakable_blocks_.emplace_back(nullptr, NoTarget);
|
||||
ValueTypeCode type;
|
||||
switch (TypeOf(expr)) {
|
||||
case kWasmI32:
|
||||
@ -1969,7 +1976,7 @@ class AsmWasmBuilderImpl final : public AstVisitor<AsmWasmBuilderImpl> {
|
||||
AsmTyper* typer_;
|
||||
bool typer_failed_;
|
||||
bool typer_finished_;
|
||||
ZoneVector<std::pair<BreakableStatement*, bool>> breakable_blocks_;
|
||||
ZoneVector<std::pair<BreakableStatement*, TargetType>> breakable_blocks_;
|
||||
ZoneVector<ForeignVariable> foreign_variables_;
|
||||
WasmFunctionBuilder* init_function_;
|
||||
WasmFunctionBuilder* foreign_init_function_;
|
||||
|
@ -410,6 +410,22 @@ function TestContinueInNamedWhile() {
|
||||
assertWasm(20, TestContinueInNamedWhile);
|
||||
|
||||
|
||||
function TestContinueInDoWhileFalse() {
|
||||
"use asm";
|
||||
|
||||
function caller() {
|
||||
do {
|
||||
continue;
|
||||
} while (false);
|
||||
return 47;
|
||||
}
|
||||
|
||||
return {caller: caller};
|
||||
}
|
||||
|
||||
assertWasm(47, TestContinueInDoWhileFalse);
|
||||
|
||||
|
||||
function TestNot() {
|
||||
"use asm";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user