github.com/goccy/go-jit@v0.0.0-20200514131505-ff78d45cf6af/internal/ccall/jit-live.c (about)

     1  /*
     2   * jit-live.c - Liveness analysis for function bodies.
     3   *
     4   * Copyright (C) 2004  Southern Storm Software, Pty Ltd.
     5   *
     6   * This file is part of the libjit library.
     7   *
     8   * The libjit library is free software: you can redistribute it and/or
     9   * modify it under the terms of the GNU Lesser General Public License
    10   * as published by the Free Software Foundation, either version 2.1 of
    11   * the License, or (at your option) any later version.
    12   *
    13   * The libjit library is distributed in the hope that it will be useful,
    14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    16   * Lesser General Public License for more details.
    17   *
    18   * You should have received a copy of the GNU Lesser General Public
    19   * License along with the libjit library.  If not, see
    20   * <http://www.gnu.org/licenses/>.
    21   */
    22  
    23  #include "jit-internal.h"
    24  #include <jit/jit-dump.h>
    25  
    26  #define USE_FORWARD_PROPAGATION 1
    27  #define USE_BACKWARD_PROPAGATION 1
    28  
    29  /*
    30   * Compute liveness information for a basic block.
    31   */
    32  static void
    33  compute_liveness_for_block(jit_block_t block)
    34  {
    35  	jit_insn_iter_t iter;
    36  	jit_insn_t insn;
    37  	jit_value_t dest;
    38  	jit_value_t value1;
    39  	jit_value_t value2;
    40  	int flags;
    41  
    42  	/* Scan backwards to compute the liveness flags */
    43  	jit_insn_iter_init_last(&iter, block);
    44  	while((insn = jit_insn_iter_previous(&iter)) != 0)
    45  	{
    46  		/* Skip NOP instructions, which may have arguments left
    47  		   over from when the instruction was replaced, but which
    48  		   are not relevant to our liveness analysis */
    49  		if(insn->opcode == JIT_OP_NOP)
    50  		{
    51  			continue;
    52  		}
    53  
    54  		/* Fetch the value parameters to this instruction */
    55  		flags = insn->flags;
    56  		if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
    57  		{
    58  			dest = insn->dest;
    59  			if(dest && dest->is_constant)
    60  			{
    61  				dest = 0;
    62  			}
    63  		}
    64  		else
    65  		{
    66  			dest = 0;
    67  		}
    68  		if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
    69  		{
    70  			value1 = insn->value1;
    71  			if(value1 && value1->is_constant)
    72  			{
    73  				value1 = 0;
    74  			}
    75  		}
    76  		else
    77  		{
    78  			value1 = 0;
    79  		}
    80  		if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
    81  		{
    82  			value2 = insn->value2;
    83  			if(value2 && value2->is_constant)
    84  			{
    85  				value2 = 0;
    86  			}
    87  		}
    88  		else
    89  		{
    90  			value2 = 0;
    91  		}
    92  
    93  		/* Record the liveness information in the instruction flags */
    94  		flags &= ~JIT_INSN_LIVENESS_FLAGS;
    95  		if(dest)
    96  		{
    97  			if(dest->live)
    98  			{
    99  				flags |= JIT_INSN_DEST_LIVE;
   100  			}
   101  			if(dest->next_use)
   102  			{
   103  				flags |= JIT_INSN_DEST_NEXT_USE;
   104  			}
   105  		}
   106  		if(value1)
   107  		{
   108  			if(value1->live)
   109  			{
   110  				flags |= JIT_INSN_VALUE1_LIVE;
   111  			}
   112  			if(value1->next_use)
   113  			{
   114  				flags |= JIT_INSN_VALUE1_NEXT_USE;
   115  			}
   116  		}
   117  		if(value2)
   118  		{
   119  			if(value2->live)
   120  			{
   121  				flags |= JIT_INSN_VALUE2_LIVE;
   122  			}
   123  			if(value2->next_use)
   124  			{
   125  				flags |= JIT_INSN_VALUE2_NEXT_USE;
   126  			}
   127  		}
   128  		insn->flags = (short)flags;
   129  
   130  		/* Set the destination to "not live, no next use" */
   131  		if(dest)
   132  		{
   133  			if((flags & JIT_INSN_DEST_IS_VALUE) == 0)
   134  			{
   135  				if(!(dest->next_use) && !(dest->live))
   136  				{
   137  					/* There is no next use of this value and it is not
   138  					   live on exit from the block.  So we can discard
   139  					   the entire instruction as it will have no effect */
   140  #ifdef _JIT_COMPILE_DEBUG
   141  					printf("liveness analysis: optimize away instruction '");
   142  					jit_dump_insn(stdout, block->func, insn);
   143  					printf("'\n");
   144  #endif
   145  					insn->opcode = (short)JIT_OP_NOP;
   146  					continue;
   147  				}
   148  				dest->live = 0;
   149  				dest->next_use = 0;
   150  			}
   151  			else
   152  			{
   153  				/* The destination is actually a source value for this
   154  				   instruction (e.g. JIT_OP_STORE_RELATIVE_*) */
   155  				dest->live = 1;
   156  				dest->next_use = 1;
   157  			}
   158  		}
   159  
   160  		/* Set value1 and value2 to "live, next use" */
   161  		if(value1)
   162  		{
   163  			value1->live = 1;
   164  			value1->next_use = 1;
   165  		}
   166  		if(value2)
   167  		{
   168  			value2->live = 1;
   169  			value2->next_use = 1;
   170  		}
   171  	}
   172  }
   173  
   174  #if defined(USE_FORWARD_PROPAGATION) || defined(USE_BACKWARD_PROPAGATION)
   175  /*
   176   * Check if the instruction is eligible for copy propagation.
   177   */
   178  static int
   179  is_copy_insn(jit_insn_t insn)
   180  {
   181  	jit_type_t dtype;
   182  	jit_type_t vtype;
   183  
   184  	if (!insn || !insn->dest || !insn->value1)
   185  	{
   186  		return 0;
   187  	}
   188  
   189  	switch(insn->opcode)
   190  	{
   191  	case JIT_OP_COPY_INT:
   192  		/* Currently JIT_INSN_COPY_INT is used not only for int-to-int
   193  		   copying but for byte-to-int and short-to-int copying too
   194  		   (see jit_insn_convert). Propagation of byte and short values
   195  		   to instructions that expect ints might confuse them. */
   196  		dtype = jit_type_normalize(insn->dest->type);
   197  		vtype = jit_type_normalize(insn->value1->type);
   198  		if(dtype != vtype)
   199  		{
   200  			/* signed/unsigned int conversion should be safe */
   201  			if((dtype->kind == JIT_TYPE_INT || dtype->kind == JIT_TYPE_UINT)
   202  			   && (vtype->kind == JIT_TYPE_INT || vtype->kind == JIT_TYPE_UINT))
   203  			{
   204  				return 1;
   205  			}
   206  			return 0;
   207  		}
   208  		return 1;
   209  
   210  	case JIT_OP_COPY_LOAD_SBYTE:
   211  	case JIT_OP_COPY_LOAD_UBYTE:
   212  	case JIT_OP_COPY_LOAD_SHORT:
   213  	case JIT_OP_COPY_LOAD_USHORT:
   214  	case JIT_OP_COPY_LONG:
   215  	case JIT_OP_COPY_FLOAT32:
   216  	case JIT_OP_COPY_FLOAT64:
   217  	case JIT_OP_COPY_NFLOAT:
   218  	case JIT_OP_COPY_STRUCT:
   219  	case JIT_OP_COPY_STORE_BYTE:
   220  	case JIT_OP_COPY_STORE_SHORT:
   221  		return 1;
   222  	}
   223  
   224  	return 0;
   225  }
   226  #endif
   227  
   228  #if USE_FORWARD_PROPAGATION
   229  /*
   230   * Perform simple copy propagation within basic block. Replaces instructions
   231   * that look like this:
   232   *
   233   * i) t = x
   234   * ...
   235   * j) y = op(t)
   236   *
   237   * with the folowing:
   238   *
   239   * i) t = x
   240   * ...
   241   * j) y = op(x)
   242   *
   243   * If "t" is not used after the instruction "j" then further liveness analysis
   244   * may replace the instruction "i" with a noop:
   245   *
   246   * i) noop
   247   * ...
   248   * j) y = op(x)
   249   *
   250   * The propagation stops as soon as either "t" or "x" are changed (used as a
   251   * dest in a different instruction).
   252   */
   253  static int
   254  forward_propagation(jit_block_t block)
   255  {
   256  	int optimized;
   257  	jit_insn_iter_t iter, iter2;
   258  	jit_insn_t insn, insn2;
   259  	jit_value_t dest, value;
   260  	int flags2;
   261  
   262  	optimized = 0;
   263  
   264  	jit_insn_iter_init(&iter, block);
   265  	while((insn = jit_insn_iter_next(&iter)) != 0)
   266  	{
   267  		if(!is_copy_insn(insn))
   268  		{
   269  			continue;
   270  		}
   271  
   272  		dest = insn->dest;
   273  		value = insn->value1;
   274  
   275  		/* Discard copy to itself */
   276  		if(dest == value)
   277  		{
   278  #ifdef _JIT_COMPILE_DEBUG
   279  			printf("forward copy propagation: optimize away copy to itself in '");
   280  			jit_dump_insn(stdout, block->func, insn);
   281  			printf("'\n");
   282  #endif
   283  			insn->opcode = (short)JIT_OP_NOP;
   284  			optimized = 1;
   285  			continue;
   286  		}
   287  
   288  		/* Not smart enough to tell when it is safe to optimize copying
   289  		   to a value that is used in other basic blocks or may be
   290  		   aliased. */
   291  		if(!dest->is_temporary)
   292  		{
   293  			continue;
   294  		}
   295  		if(dest->is_addressable || dest->is_volatile)
   296  		{
   297  			continue;
   298  		}
   299  		if(value->is_addressable || value->is_volatile)
   300  		{
   301  			continue;
   302  		}
   303  
   304  		iter2 = iter;
   305  		while((insn2 = jit_insn_iter_next(&iter2)) != 0)
   306  		{
   307  			/* Skip NOP instructions, which may have arguments left
   308  			   over from when the instruction was replaced, but which
   309  			   are not relevant to our analysis */
   310  			if(insn->opcode == JIT_OP_NOP)
   311  			{
   312  				continue;
   313  			}
   314  
   315  			flags2 = insn2->flags;
   316  			if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0)
   317  			{
   318  				if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0)
   319  				{
   320  					if(insn2->dest == dest || insn2->dest == value)
   321  					{
   322  						break;
   323  					}
   324  				}
   325  				else if(insn2->dest == dest)
   326  				{
   327  #ifdef _JIT_COMPILE_DEBUG
   328  					printf("forward copy propagation: in '");
   329  					jit_dump_insn(stdout, block->func, insn2);
   330  					printf("' replace ");
   331  					jit_dump_value(stdout, block->func, insn2->dest, 0);
   332  					printf(" with ");
   333  					jit_dump_value(stdout, block->func, value, 0);
   334  					printf("'\n");
   335  #endif
   336  					insn2->dest = value;
   337  					optimized = 1;
   338  				}
   339  			}
   340  			if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
   341  			{
   342  				if(insn2->value1 == dest)
   343  				{
   344  #ifdef _JIT_COMPILE_DEBUG
   345  					printf("forward copy propagation: in '");
   346  					jit_dump_insn(stdout, block->func, insn2);
   347  					printf("' replace ");
   348  					jit_dump_value(stdout, block->func, insn2->value1, 0);
   349  					printf(" with ");
   350  					jit_dump_value(stdout, block->func, value, 0);
   351  					printf("'\n");
   352  #endif
   353  					insn2->value1 = value;
   354  					optimized = 1;
   355  				}
   356  			}
   357  			if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
   358  			{
   359  				if(insn2->value2 == dest)
   360  				{
   361  #ifdef _JIT_COMPILE_DEBUG
   362  					printf("forward copy propagation: in '");
   363  					jit_dump_insn(stdout, block->func, insn2);
   364  					printf("' replace ");
   365  					jit_dump_value(stdout, block->func, insn2->value2, 0);
   366  					printf(" with ");
   367  					jit_dump_value(stdout, block->func, value, 0);
   368  					printf("'\n");
   369  #endif
   370  					insn2->value2 = value;
   371  					optimized = 1;
   372  				}
   373  			}
   374  		}
   375  	}
   376  
   377  	return optimized;
   378  }
   379  #endif
   380  
   381  #ifdef USE_BACKWARD_PROPAGATION
   382  /*
   383   * Perform simple copy propagation within basic block for the case when a
   384   * temporary value is stored to another value. This replaces instructions
   385   * that look like this:
   386   *
   387   * i) t = op(x)
   388   * ...
   389   * j) y = t
   390   *
   391   * with the following
   392   *
   393   * i) y = op(x)
   394   * ...
   395   * j) noop
   396   *
   397   * This is only allowed if "t" is used only in the instructions "i" and "j"
   398   * and "y" is not used between "i" and "j" (but can be used after "j").
   399   */
   400  static int
   401  backward_propagation(jit_block_t block)
   402  {
   403  	int optimized;
   404  	jit_insn_iter_t iter, iter2;
   405  	jit_insn_t insn, insn2;
   406  	jit_value_t dest, value;
   407  	int flags2;
   408  
   409  	optimized = 0;
   410  
   411  	jit_insn_iter_init_last(&iter, block);
   412  	while((insn = jit_insn_iter_previous(&iter)) != 0)
   413  	{
   414  		if(!is_copy_insn(insn))
   415  		{
   416  			continue;
   417  		}
   418  
   419  		dest = insn->dest;
   420  		value = insn->value1;
   421  
   422  		/* Discard copy to itself */
   423  		if(dest == value)
   424  		{
   425  #ifdef _JIT_COMPILE_DEBUG
   426  			printf("backward copy propagation: optimize away copy to itself in '");
   427  			jit_dump_insn(stdout, block->func, insn);
   428  			printf("'\n");
   429  #endif
   430  			insn->opcode = (short)JIT_OP_NOP;
   431  			optimized = 1;
   432  			continue;
   433  		}
   434  
   435  		/* "value" is used afterwards so we cannot eliminate it here */
   436  		if((insn->flags & (JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE)) != 0)
   437  		{
   438  			continue;
   439  		}
   440  
   441  		if(dest->is_addressable || dest->is_volatile)
   442  		{
   443  			continue;
   444  		}
   445  		if(value->is_addressable || value->is_volatile)
   446  		{
   447  			continue;
   448  		}
   449  
   450  		iter2 = iter;
   451  		while((insn2 = jit_insn_iter_previous(&iter2)) != 0)
   452  		{
   453  			/* Skip NOP instructions, which may have arguments left
   454  			   over from when the instruction was replaced, but which
   455  			   are not relevant to our analysis */
   456  			if(insn->opcode == JIT_OP_NOP)
   457  			{
   458  				continue;
   459  			}
   460  
   461  			flags2 = insn2->flags;
   462  			if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0)
   463  			{
   464  				if(insn2->dest == dest)
   465  				{
   466  					break;
   467  				}
   468  				if(insn2->dest == value)
   469  				{
   470  					if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0)
   471  					{
   472  #ifdef _JIT_COMPILE_DEBUG
   473  						printf("backward copy propagation: in '");
   474  						jit_dump_insn(stdout, block->func, insn2);
   475  						printf("' replace ");
   476  						jit_dump_value(stdout, block->func, insn2->dest, 0);
   477  						printf(" with ");
   478  						jit_dump_value(stdout, block->func, dest, 0);
   479  						printf(" and optimize away '");
   480  						jit_dump_insn(stdout, block->func, insn);
   481  						printf("'\n");
   482  #endif
   483  						insn->opcode = (short)JIT_OP_NOP;
   484  						insn2->dest = dest;
   485  						optimized = 1;
   486  					}
   487  					break;
   488  				}
   489  			}
   490  			if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
   491  			{
   492  				if(insn2->value1 == dest || insn2->value1 == value)
   493  				{
   494  					break;
   495  				}
   496  			}
   497  			if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
   498  			{
   499  				if(insn2->value2 == dest || insn2->value1 == value)
   500  				{
   501  					break;
   502  				}
   503  			}
   504  		}
   505  	}
   506  
   507  	return optimized;
   508  }
   509  #endif
   510  
   511  /* Reset value liveness flags. */
   512  static void
   513  reset_value_liveness(jit_value_t value)
   514  {
   515  	if(value)
   516  	{
   517  		if (!value->is_constant && !value->is_temporary)
   518  		{
   519  			value->live = 1;
   520  		}
   521  		else
   522  		{
   523  			value->live = 0;
   524  		}
   525  		value->next_use = 0;
   526  	}
   527  }
   528  
   529  /*
   530   * Re-scan the block to reset the liveness flags on all non-temporaries
   531   * because we need them in the original state for the next block.
   532   */
   533  static void
   534  reset_liveness_flags(jit_block_t block, int reset_all)
   535  {
   536  	jit_insn_iter_t iter;
   537  	jit_insn_t insn;
   538  	int flags;
   539  
   540  	jit_insn_iter_init(&iter, block);
   541  	while((insn = jit_insn_iter_next(&iter)) != 0)
   542  	{
   543  		flags = insn->flags;
   544  		if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
   545  		{
   546  			reset_value_liveness(insn->dest);
   547  		}
   548  		if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
   549  		{
   550  			reset_value_liveness(insn->value1);
   551  		}
   552  		if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
   553  		{
   554  			reset_value_liveness(insn->value2);
   555  		}
   556  		if(reset_all)
   557  		{
   558  			flags &= ~(JIT_INSN_DEST_LIVE | JIT_INSN_DEST_NEXT_USE
   559  				   |JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE
   560  				   |JIT_INSN_VALUE2_LIVE | JIT_INSN_VALUE2_NEXT_USE);
   561  		}
   562  	}
   563  }
   564  
   565  void _jit_function_compute_liveness(jit_function_t func)
   566  {
   567  	jit_block_t block = func->builder->entry_block;
   568  	while(block != 0)
   569  	{
   570  #ifdef USE_FORWARD_PROPAGATION
   571  		/* Perform forward copy propagation for the block */
   572  		forward_propagation(block);
   573  #endif
   574  
   575  		/* Reset the liveness flags for the next block */
   576  		reset_liveness_flags(block, 0);
   577  
   578  		/* Compute the liveness flags for the block */
   579  		compute_liveness_for_block(block);
   580  
   581  #ifdef USE_BACKWARD_PROPAGATION
   582  		/* Perform backward copy propagation for the block */
   583  		if(backward_propagation(block))
   584  		{
   585  			/* Reset the liveness flags and compute them again */
   586  			reset_liveness_flags(block, 1);
   587  			compute_liveness_for_block(block);
   588  		}
   589  #endif
   590  
   591  		/* Move on to the next block in the function */
   592  		block = block->next;
   593  	}
   594  }