File 1023-Undo-instruction-combination-optimizations.patch of Package erlang

From f302db9ba2d1e83f8349a1f376cd5c101310ecaa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Wed, 10 May 2023 08:18:15 +0200
Subject: [PATCH 3/5] Undo instruction combination optimizations

As a refactoring preparing to generate C code, undo some of
optimizations that combine instructions in the transform
engine.
---
 erts/emulator/beam/beam_transform_engine.c |  93 ++-------------
 erts/emulator/utils/beam_makeops           | 128 +++++----------------
 2 files changed, 38 insertions(+), 183 deletions(-)

diff --git a/erts/emulator/beam/beam_transform_engine.c b/erts/emulator/beam/beam_transform_engine.c
index 3ce934a48c..7891c901df 100644
--- a/erts/emulator/beam/beam_transform_engine.c
+++ b/erts/emulator/beam/beam_transform_engine.c
@@ -83,51 +83,17 @@ erts_transform_engine(LoaderState* st)
 	    if (((1 << instr->a[ap].type) & mask) == 0)
 		goto restart;
 	    break;
-#if defined(TOP_is_type_next_arg)
-	case TOP_is_type_next_arg:
-	    mask = *pc++;
-	    ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-            ap++;
-	    break;
-#endif
 	case TOP_pred:
 	    i = *pc++;
             i = erts_beam_eval_predicate((unsigned) i, st, var, rest_args);
 	    if (i == 0)
 		goto restart;
 	    break;
-#if defined(TOP_is_eq)
 	case TOP_is_eq:
 	    ASSERT(ap < instr->arity);
 	    if (*pc++ != instr->a[ap].val)
 		goto restart;
 	    break;
-#endif
-	case TOP_is_type_eq:
-	    mask = *pc++;
-
-	    ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-	    if (*pc++ != instr->a[ap].val)
-		goto restart;
-	    break;
-#if defined(TOP_is_type_eq_next_arg)
-	case TOP_is_type_eq_next_arg:
-	    mask = *pc++;
-            ASSERT(ap < instr->arity);
-            ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-            if (((1 << instr->a[ap].type) & mask) == 0)
-                goto restart;
-            if (*pc++ != instr->a[ap].val)
-                goto restart;
-            ap++;
-            break;
-#endif
 #if defined(TOP_is_bif)
 	case TOP_is_bif:
 	    {
@@ -249,50 +215,12 @@ erts_transform_engine(LoaderState* st)
 	    }
 	    break;
 #endif
-	case TOP_set_var_next_arg:
+	case TOP_set_var:
 	    ASSERT(ap < instr->arity);
 	    i = *pc++;
 	    ASSERT(i < TE_MAX_VARS);
-	    var[i].type = instr->a[ap].type;
-	    var[i].val = instr->a[ap].val;
-	    ap++;
-	    break;
-#if defined(TOP_is_type_set_var_next_arg)
-	case TOP_is_type_set_var_next_arg:
-            mask = pc[0];
-            i = pc[1];
-	    ASSERT(i < TE_MAX_VARS);
-            ASSERT(ap < instr->arity);
-	    ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-	    if (((1 << instr->a[ap].type) & mask) == 0)
-		goto restart;
-	    ASSERT(i < TE_MAX_VARS);
 	    var[i] = instr->a[ap];
-	    ap++;
-            pc += 2;
-	    break;
-#endif
-#if defined(TOP_is_type_eq_set_var_next_arg)
-	case TOP_is_type_eq_set_var_next_arg:
-            {
-                Eterm val;
-                mask = pc[0];
-                val = pc[1];
-                i = pc[2];
-                ASSERT(i < TE_MAX_VARS);
-                ASSERT(ap < instr->arity);
-                ASSERT(instr->a[ap].type < BEAM_NUM_TAGS);
-                if (((1 << instr->a[ap].type) & mask) == 0)
-                    goto restart;
-                if (val != instr->a[ap].val)
-                    goto restart;
-                ASSERT(i < TE_MAX_VARS);
-                var[i] = instr->a[ap];
-                ap++;
-                pc += 3;
-            }
 	    break;
-#endif
 #if defined(TOP_rest_args)
 	case TOP_rest_args:
 	    {
@@ -331,8 +259,8 @@ erts_transform_engine(LoaderState* st)
 	    keep = instr;
 	    break;
 #endif
-#if defined(TOP_call_end)
-	case TOP_call_end:
+#if defined(TOP_call)
+	case TOP_call:
 	    {
 		BeamOp** lastp;
 		BeamOp* new_instr;
@@ -352,7 +280,7 @@ erts_transform_engine(LoaderState* st)
 		*lastp = keep;
                 instr = new_instr;
 	    }
-	    /* FALLTHROUGH */
+            break;
 #endif
 	case TOP_end:
             st->genop = instr;
@@ -382,18 +310,15 @@ erts_transform_engine(LoaderState* st)
 	    instr->arity = gen_opc[op].arity;
 	    return TE_OK;
 #endif
-	case TOP_store_val_next_arg:
+	case TOP_store_val:
             instr->a[ap].type = pc[0];
             instr->a[ap].val = pc[1];
-            ap++;
             pc += 2;
             break;
-	case TOP_store_var_next_arg:
+	case TOP_store_var:
 	    i = *pc++;
 	    ASSERT(i < TE_MAX_VARS);
-	    instr->a[ap].type = var[i].type;
-	    instr->a[ap].val = var[i].val;
-	    ap++;
+	    instr->a[ap] = var[i];
 	    break;
 #if defined(TOP_store_rest_args)
 	case TOP_store_rest_args:
@@ -416,6 +341,10 @@ erts_transform_engine(LoaderState* st)
 	case TOP_try_me_else_fail:
 	    restart = restart_fail;
 	    break;
+#if defined(TOP_nop)
+	case TOP_nop:
+            break;
+#endif
 	case TOP_fail:
 	    return TE_FAIL;
 #if defined(TOP_skip_unless)
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index c7ea032026..57f9d0fe49 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -2835,13 +2835,7 @@ sub tr_gen_from {
 			$types .= "$_ ";
 			$type_mask |= $type_bit{$_};
 		    }
-		    if ($cond ne 'is_eq') {
-			push(@code, make_op($types, 'is_type', $type_mask));
-		    } else {
-			$cond = '';
-			push(@code, make_op("$types== $val", 'is_type_eq',
-                                            $type_mask, $val));
-		    }
+                    push(@code, make_op($types, 'is_type', $type_mask));
 		}
 	    }
 
@@ -2883,14 +2877,7 @@ sub tr_gen_from {
 		    push(@code, make_op($var, 'set_var', $var{$var}));
 		}
 	    }
-	    if (is_instr($code[$#code], 'set_var')) {
-		my $ref = pop @code;
-		my $comment = $ref->[2];
-		my $var = $ref->[1][1];
-		push(@code, make_op($comment, 'set_var_next_arg', $var));
-	    } else {
-		push(@code, make_op($ignored_var, 'next_arg'));
-	    }
+            push(@code, make_op($ignored_var, 'next_arg'));
 	}
 
 	# Remove redundant 'next_arg' instructions before the end
@@ -2903,39 +2890,6 @@ sub tr_gen_from {
     #
     push(@code, make_op($may_fail ? '' : 'always reached', 'commit'));
 
-    #
-    # Peephole optimization: combine instructions.
-    #
-    for (my $i = 0; $i < @code; $i++) {
-        if (is_instr($code[$i], 'is_type')) {
-            my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
-            if (is_instr($code[$i+1], 'set_var_next_arg')) {
-                my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
-                my $comment = "$type_comment $next_comment";
-                my $op = make_op($comment, 'is_type_set_var_next_arg',
-                                 $is_type_ref->[1], $next_ref->[1]);
-                splice @code, $i, 2, ($op);
-            } elsif (is_instr($code[$i+1], 'next_arg')) {
-                my $op = make_op($type_comment, 'is_type_next_arg', $is_type_ref->[1]);
-                splice @code, $i, 2, ($op);
-            }
-        } elsif (is_instr($code[$i], 'is_type_eq')) {
-            my(undef, $is_type_ref, $type_comment) = @{$code[$i]};
-            if (is_instr($code[$i+1], 'set_var_next_arg')) {
-                my(undef, $next_ref, $next_comment) = @{$code[$i+1]};
-                my $comment = "$type_comment $next_comment";
-                my $op = make_op($comment, 'is_type_eq_set_var_next_arg',
-                                 $is_type_ref->[1], $is_type_ref->[2],
-                                 $next_ref->[1]);
-                splice @code, $i, 2, ($op);
-            } elsif (is_instr($code[$i+1], 'next_arg')) {
-                my $op = make_op($type_comment, 'is_type_eq_next_arg',
-                                 $is_type_ref->[1], $is_type_ref->[2]);
-                splice @code, $i, 2, ($op);
-            }
-        }
-    }
-
     $te_max_vars = $var_num
 	if $te_max_vars < $var_num;
     [\%var, \%var_type, \@instrs, \@code];
@@ -2984,7 +2938,7 @@ sub tr_gen_to {
 	    pop(@code);	# Get rid of 'commit' instruction
 	    my $index = next_tr_index(\@call_table, \%call_table,
 				      $name, @args);
-	    my $op = make_op("$name()", 'call_end', $index);
+	    my $op = make_op("$name()", 'call', $index);
 	    my @slots = grep(/^\d+/, map { $var{$_} } @ops);
 	    op_slot_usage($op, @slots);
 	    push(@code, $op);
@@ -3013,21 +2967,22 @@ sub tr_gen_to {
 	    } elsif ($var ne '') {
 		error($where, "variable '$var' unbound")
 		    unless defined $var{$var};
-		my $op = make_op($var, 'store_var_next_arg', $var{$var});
+		my $op = make_op($var, 'store_var', $var{$var});
 		op_slot_usage($op, $var{$var});
-		push(@code, $op);
+                my $next_arg = make_op('', 'next_arg');
+		push @code, $op, $next_arg;
 	    } elsif ($type ne '') {
                 my $val = $type_val || 0;
                 my $comment = "$type=$val";
-                my $op = make_op($comment, 'store_val_next_arg', "TAG_$type", $val);
-                push @code, $op;
+                my $store_val = make_op($comment, 'store_val', "TAG_$type", $val);
+                my $next_arg = make_op('', 'next_arg');
+                push @code, $store_val, $next_arg;
 	    }
 	}
 	pop(@code) if is_instr($code[$#code], 'next_arg');
     }
 
-    push(@code, make_op('', 'end'))
-	unless is_instr($code[$#code], 'call_end');
+    push(@code, make_op('', 'end'));
 
     tr_maybe_keep(\@code);
     tr_maybe_rename(\@code);
@@ -3162,7 +3117,8 @@ sub group_tr {
 
 sub tr_maybe_keep {
     my($ref) = @_;
-    my @last_instr;
+    my $ap = 1;
+    my @last_instr = ('unknown','ignored');
     my $pos;
     my $reused_instr;
 
@@ -3171,21 +3127,21 @@ sub tr_maybe_keep {
 	my($size, $instr_ref, $comment) = @$instr;
 	my($op, @args) = @$instr_ref;
 	if ($op eq 'next_instr') {
-	    @last_instr = ($args[0]);
-	} elsif ($op eq 'set_var_next_arg') {
-	    push @last_instr, $args[0];
-	} elsif ($op eq 'is_type_set_var_next_arg') {
-	    push @last_instr, $args[1];
-	} elsif ($op eq 'is_type_eq_set_var_next_arg') {
-	    push @last_instr, $args[2];
+            @last_instr = ($args[0],'ignored');
+            $ap = 1;
 	} elsif ($op eq 'next_arg') {
-	    push @last_instr, 'ignored';
+            $ap++;
+            $last_instr[$ap] = 'ignored'
+                unless defined $pos;
+	} elsif ($op eq 'set_var') {
+	    $last_instr[$ap] = $args[0];
 	} elsif ($op eq 'new_instr') {
 	    unless (defined $pos) {
 		# 'new_instr' immediately after 'commit'.
 		$reused_instr = $args[0];
-		return unless shift(@last_instr) == $reused_instr;
+		return unless $last_instr[0] == $reused_instr;
 		$pos = $i - 1;
+                $ap = 1;
 	    } else {
 		# Second 'new_instr' after 'commit'. The instructions
 		# from $pos up to and including $i - 1 rebuilds the
@@ -3196,8 +3152,8 @@ sub tr_maybe_keep {
 		splice @$ref, $pos, $i-$pos, ($reuse);
 		return;
 	    }
-	} elsif ($op eq 'store_var_next_arg') {
-	    return unless @last_instr and shift(@last_instr) eq $args[0];
+	} elsif ($op eq 'store_var') {
+	    return unless $last_instr[$ap] eq $args[0];
 	} elsif (defined $pos) {
 	    return;
 	}
@@ -3235,26 +3191,7 @@ sub tr_maybe_rename {
 	my($op, @args) = @$instr_ref;
 
 	if ($s eq 'left') {
-	    if ($op eq 'set_var_next_arg') {
-		if ($num_args == $a and $args[0] == $a) {
-		    $num_args++;
-		}
-		$a++;
-            } elsif ($op eq 'is_type_set_var_next_arg') {
-	        if ($num_args == $a and $args[1] == $a) {
-	            $num_args++;
-	        }
-	        $a++;
-            } elsif ($op eq 'is_type_eq_set_var_next_arg') {
-	        if ($num_args == $a and $args[2] == $a) {
-	            $num_args++;
-	        }
-	        $a++;
-	    } elsif ($op eq 'next_arg') {
-		$a++;
-	    } elsif ($op eq 'is_type_next_arg') {
-		$a++;
-	    } elsif ($op eq 'is_type_eq_next_arg') {
+            if ($op eq 'next_arg') {
 		$a++;
 	    } elsif ($op eq 'commit') {
 		$a = 0;
@@ -3272,9 +3209,7 @@ sub tr_maybe_rename {
 		return;
 	    }
 	} elsif ($s eq 'right') {
-	    if ($op eq 'store_var_next_arg' && $args[0] == $a) {
-		$a++;
-	    } elsif ($op eq 'end' && $a <= $num_args) {
+            if ($op eq 'end' && $a <= $num_args) {
 		my $name = $gen_opname[$new_instr];
 		my $arity = $gen_arity[$new_instr];
 		my $new_op = make_op("$name/$arity", 'rename', $new_instr);
@@ -3303,19 +3238,10 @@ sub tr_remove_unused {
     for my $instr (@$ref) {
 	my($size, $instr_ref, $comment) = @$instr;
 	my($op, @args) = @$instr_ref;
-	if ($op eq 'set_var_next_arg') {
+	if ($op eq 'set_var') {
 	    my $var = $args[0];
 	    next if $used{$var};
-	    $instr = make_op("$comment (ignored)", 'next_arg');
-	} elsif ($op eq 'is_type_set_var_next_arg') {
-            my($type,$var) = @args;
-            next if $used{$var};
-	    $instr = make_op("$comment (ignored)", 'is_type_next_arg', $type);
-	} elsif ($op eq 'is_type_eq_set_var_next_arg') {
-            my($type,$val,$var) = @args;
-            next if $used{$var};
-            $instr = make_op("$comment (ignored)", 'is_type_eq_next_arg',
-                             $type, $val);
+	    $instr = make_op("(ignoring unused variable $comment)", 'nop');
         }
     }
 
-- 
2.35.3

openSUSE Build Service is sponsored by