[vm/compiler] first vm implementation of block expression

Rationale:
Implementation of control-flow collections is
done through the notion of block expressions.
This is a first implementation to get this
working. It takes a few shortcuts (like disabling
OSR inside block expressions for now) that may be
refined later.

https://github.com/dart-lang/language/issues/78
https://github.com/dart-lang/language/issues/47

Change-Id: I966bf10942075052fcfd9bac00298a179efc551b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/94441
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/vm/lib/transformations/type_flow/summary_collector.dart b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
index f4e50e7..23f8d8b 100644
--- a/pkg/vm/lib/transformations/type_flow/summary_collector.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary_collector.dart
@@ -870,6 +870,12 @@
   }
 
   @override
+  TypeExpr visitBlockExpression(BlockExpression node) {
+    _visit(node.body);
+    return _visit(node.value);
+  }
+
+  @override
   TypeExpr visitListLiteral(ListLiteral node) {
     node.expressions.forEach(_visit);
     Class concreteClass =
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 7a3404e..ac67edd 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -1338,10 +1338,8 @@
       return BuildFunctionExpression();
     case kLet:
       return BuildLet(position);
-    case kBlockExpression: {
-      UNIMPLEMENTED();
-      break;
-    }
+    case kBlockExpression:
+      return BuildBlockExpression();
     case kBigIntLiteral:
       return BuildBigIntLiteral(position);
     case kStringLiteral:
@@ -1484,6 +1482,18 @@
   --flow_graph_builder_->try_depth_;
 }
 
+intptr_t StreamingFlowGraphBuilder::block_expression_depth() {
+  return flow_graph_builder_->block_expression_depth_;
+}
+
+void StreamingFlowGraphBuilder::block_expression_depth_inc() {
+  ++flow_graph_builder_->block_expression_depth_;
+}
+
+void StreamingFlowGraphBuilder::block_expression_depth_dec() {
+  --flow_graph_builder_->block_expression_depth_;
+}
+
 intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() {
   return flow_graph_builder_->CurrentTryIndex();
 }
@@ -1838,7 +1848,7 @@
   const intptr_t saved_context_depth = B->context_depth_;
   const ProgramState state(B->breakable_block_, B->switch_block_,
                            B->loop_depth_, B->for_in_depth_, B->try_depth_,
-                           B->catch_depth_);
+                           B->catch_depth_, B->block_expression_depth_);
 
   Fragment instructions;
 
@@ -3798,6 +3808,24 @@
   return instructions;
 }
 
+Fragment StreamingFlowGraphBuilder::BuildBlockExpression() {
+  block_expression_depth_inc();
+  const intptr_t offset = ReaderOffset() - 1;  // Include the tag.
+
+  Fragment instructions;
+
+  instructions += EnterScope(offset);
+  const intptr_t list_length = ReadListLength();  // read number of statements.
+  for (intptr_t i = 0; i < list_length; ++i) {
+    instructions += BuildStatement();  // read ith statement.
+  }
+  instructions += BuildExpression();  // read expression (inside scope).
+  instructions += ExitScope(offset);
+
+  block_expression_depth_dec();
+  return instructions;
+}
+
 Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral(
     TokenPosition* position) {
   if (position != NULL) *position = TokenPosition::kNoSource;
@@ -4134,6 +4162,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildWhileStatement() {
+  ASSERT(block_expression_depth() == 0);  // no while in block-expr
   loop_depth_inc();
   const TokenPosition position = ReadPosition();  // read position.
   TestFragment condition = TranslateConditionForControl();  // read condition.
@@ -4161,6 +4190,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildDoStatement() {
+  ASSERT(block_expression_depth() == 0);  // no do-while in block-expr
   loop_depth_inc();
   const TokenPosition position = ReadPosition();  // read position.
   Fragment body = BuildStatement();               // read body.
@@ -4243,7 +4273,11 @@
     body += Goto(join);
 
     Fragment loop(join);
-    loop += CheckStackOverflow(position);
+
+    // Avoid OSR point inside block-expressions.
+    // TODO(ajcbik): make sure OSR works inside BE too
+    if (block_expression_depth() == 0) loop += CheckStackOverflow(position);
+
     if (condition.entry != nullptr) {
       loop <<= condition.entry;
     } else {
@@ -4316,7 +4350,11 @@
     body += Goto(join);
 
     Fragment loop(join);
-    loop += CheckStackOverflow(position);
+
+    // Avoid OSR point inside block-expressions.
+    // TODO(ajcbik): make sure OSR works inside BE too
+    if (block_expression_depth() == 0) loop += CheckStackOverflow(position);
+
     loop += condition;
   } else {
     instructions += condition;
@@ -4621,6 +4659,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildTryCatch() {
+  ASSERT(block_expression_depth() == 0);  // no try-catch in block-expr
   InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch");
 
   intptr_t try_handler_index = AllocateTryIndex();
@@ -4750,6 +4789,7 @@
 }
 
 Fragment StreamingFlowGraphBuilder::BuildTryFinally() {
+  ASSERT(block_expression_depth() == 0);  // no try-finally in block-expr
   // Note on streaming:
   // We only stream this TryFinally if we can stream everything inside it,
   // so creating a "TryFinallyBlock" with a kernel binary offset instead of an
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
index b12d4e3..b554501 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
@@ -112,6 +112,9 @@
   void catch_depth_dec();
   void try_depth_inc();
   void try_depth_dec();
+  intptr_t block_expression_depth();
+  void block_expression_depth_inc();
+  void block_expression_depth_dec();
   intptr_t CurrentTryIndex();
   intptr_t AllocateTryIndex();
   LocalVariable* CurrentException();
@@ -314,6 +317,7 @@
   Fragment BuildMapLiteral(bool is_const, TokenPosition* position);
   Fragment BuildFunctionExpression();
   Fragment BuildLet(TokenPosition* position);
+  Fragment BuildBlockExpression();
   Fragment BuildBigIntLiteral(TokenPosition* position);
   Fragment BuildStringLiteral(TokenPosition* position);
   Fragment BuildIntLiteral(uint8_t payload, TokenPosition* position);
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index c9cdd92..1a88f1ba 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -512,10 +512,10 @@
       CalculateVariableDeclarationFingerprint();  // read variable declaration.
       CalculateExpressionFingerprint();           // read expression.
       return;
-    case kBlockExpression: {
-      UNIMPLEMENTED();
+    case kBlockExpression:
+      CalculateStatementListFingerprint();
+      CalculateExpressionFingerprint();  // read expression.
       return;
-    }
     case kInstantiation:
       CalculateExpressionFingerprint();       // read expression.
       CalculateListOfDartTypesFingerprint();  // read type arguments.
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index 9da71ef..7fa7051 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -54,6 +54,7 @@
       try_depth_(0),
       catch_depth_(0),
       for_in_depth_(0),
+      block_expression_depth_(0),
       graph_entry_(NULL),
       scopes_(NULL),
       breakable_block_(NULL),
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index a2cb7ca..f1aeb23 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -283,6 +283,7 @@
   intptr_t try_depth_;
   intptr_t catch_depth_;
   intptr_t for_in_depth_;
+  intptr_t block_expression_depth_;
 
   GraphEntryInstr* graph_entry_;
 
@@ -355,13 +356,15 @@
                intptr_t loop_depth,
                intptr_t for_in_depth,
                intptr_t try_depth,
-               intptr_t catch_depth)
+               intptr_t catch_depth,
+               intptr_t block_expression_depth)
       : breakable_block_(breakable_block),
         switch_block_(switch_block),
         loop_depth_(loop_depth),
         for_in_depth_(for_in_depth),
         try_depth_(try_depth),
-        catch_depth_(catch_depth) {}
+        catch_depth_(catch_depth),
+        block_expression_depth_(block_expression_depth) {}
 
   void assignTo(FlowGraphBuilder* builder) const {
     builder->breakable_block_ = breakable_block_;
@@ -370,6 +373,7 @@
     builder->for_in_depth_ = for_in_depth_;
     builder->try_depth_ = try_depth_;
     builder->catch_depth_ = catch_depth_;
+    builder->block_expression_depth_ = block_expression_depth_;
   }
 
  private:
@@ -379,6 +383,7 @@
   const intptr_t for_in_depth_;
   const intptr_t try_depth_;
   const intptr_t catch_depth_;
+  const intptr_t block_expression_depth_;
 };
 
 class SwitchBlock {
@@ -505,7 +510,8 @@
                builder_->loop_depth_,
                builder_->for_in_depth_,
                builder_->try_depth_ - 1,
-               builder_->catch_depth_) {
+               builder_->catch_depth_,
+               builder_->block_expression_depth_) {
     builder_->try_finally_block_ = this;
   }
   ~TryFinallyBlock() { builder_->try_finally_block_ = outer_; }
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index d0b79b9..ebd4ed7 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2262,10 +2262,10 @@
       SkipVariableDeclaration();  // read variable declaration.
       SkipExpression();           // read expression.
       return;
-    case kBlockExpression: {
-      UNIMPLEMENTED();
+    case kBlockExpression:
+      SkipStatementList();
+      SkipExpression();  // read expression.
       return;
-    }
     case kInstantiation:
       SkipExpression();       // read expression.
       SkipListOfDartTypes();  // read type arguments.
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index 7ab81b7..8ea2448 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -840,7 +840,19 @@
       return;
     }
     case kBlockExpression: {
-      UNIMPLEMENTED();
+      PositionScope scope(&helper_.reader_);
+      intptr_t offset = helper_.ReaderOffset() - 1;  // -1 to include tag byte.
+
+      EnterScope(offset);
+
+      intptr_t list_length =
+          helper_.ReadListLength();  // read number of statements.
+      for (intptr_t i = 0; i < list_length; ++i) {
+        VisitStatement();  // read ith statement.
+      }
+      VisitExpression();  // read expression.
+
+      ExitScope(helper_.reader_.min_position(), helper_.reader_.max_position());
       return;
     }
     case kBigIntLiteral: