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

     1  /*
     2   * jit-dump.c - Functions for dumping JIT structures, for debugging.
     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-rules.h"
    25  #include <jit/jit-dump.h>
    26  #ifdef HAVE_STDLIB_H
    27  # include <stdlib.h>
    28  #endif
    29  #ifdef HAVE_UNISTD_H
    30  # include <unistd.h>
    31  #endif
    32  
    33  #if defined(JIT_BACKEND_INTERP)
    34  # include "jit-interp.h"
    35  #endif
    36  
    37  /*@
    38  
    39  @cindex jit-dump.h
    40  
    41  The library provides some functions for dumping various objects to a
    42  stream.  These functions are declared in the header
    43  @code{<jit/jit-dump.h>}.
    44  
    45  @*/
    46  
    47  /*@
    48   * @deftypefun void jit_dump_type (FILE *@var{stream}, jit_type_t @var{type})
    49   * Dump the name of a type to a stdio stream.
    50   * @end deftypefun
    51  @*/
    52  void jit_dump_type(FILE *stream, jit_type_t type)
    53  {
    54  	const char *name;
    55  	type = jit_type_remove_tags(type);
    56  	if(!type || !stream)
    57  	{
    58  		return;
    59  	}
    60  	switch(type->kind)
    61  	{
    62  		case JIT_TYPE_VOID:		name = "void"; break;
    63  		case JIT_TYPE_SBYTE:		name = "sbyte"; break;
    64  		case JIT_TYPE_UBYTE:		name = "ubyte"; break;
    65  		case JIT_TYPE_SHORT:		name = "short"; break;
    66  		case JIT_TYPE_USHORT:		name = "ushort"; break;
    67  		case JIT_TYPE_INT:		name = "int"; break;
    68  		case JIT_TYPE_UINT:		name = "uint"; break;
    69  		case JIT_TYPE_NINT:		name = "nint"; break;
    70  		case JIT_TYPE_NUINT:		name = "nuint"; break;
    71  		case JIT_TYPE_LONG:		name = "long"; break;
    72  		case JIT_TYPE_ULONG:		name = "ulong"; break;
    73  		case JIT_TYPE_FLOAT32:		name = "float32"; break;
    74  		case JIT_TYPE_FLOAT64:		name = "float64"; break;
    75  		case JIT_TYPE_NFLOAT:		name = "nfloat"; break;
    76  
    77  		case JIT_TYPE_STRUCT:
    78  		{
    79  			fprintf(stream, "struct<%u>",
    80  				(unsigned int)(jit_type_get_size(type)));
    81  			return;
    82  		}
    83  		/* Not reached */
    84  
    85  		case JIT_TYPE_UNION:
    86  		{
    87  			fprintf(stream, "union<%u>",
    88  				(unsigned int)(jit_type_get_size(type)));
    89  			return;
    90  		}
    91  		/* Not reached */
    92  
    93  		case JIT_TYPE_SIGNATURE:	name = "signature"; break;
    94  		case JIT_TYPE_PTR:		name = "ptr"; break;
    95  		default: 			name = "<unknown-type>"; break;
    96  	}
    97  	fputs(name, stream);
    98  }
    99  
   100  /*
   101   * Format an integer value of arbitrary precision.
   102   */
   103  static char *format_integer(char *buf, int is_neg, jit_ulong value)
   104  {
   105  	buf += 64;
   106  	*(--buf) = '\0';
   107  	if(value == 0)
   108  	{
   109  		*(--buf) = '0';
   110  	}
   111  	else
   112  	{
   113  		while(value != 0)
   114  		{
   115  			*(--buf) = '0' + (int)(value % 10);
   116  			value /= 10;
   117  		}
   118  	}
   119  	if(is_neg)
   120  	{
   121  		*(--buf) = '-';
   122  	}
   123  	return buf;
   124  }
   125  
   126  /*@
   127   * @deftypefun void jit_dump_value (FILE *@var{stream}, jit_function_t @var{func}, jit_value_t @var{value}, const char *@var{prefix})
   128   * Dump the name of a value to a stdio stream.  If @var{prefix} is not
   129   * NULL, then it indicates a type prefix to add to the value name.
   130   * If @var{prefix} is NULL, then this function intuits the type prefix.
   131   * @end deftypefun
   132  @*/
   133  void jit_dump_value(FILE *stream, jit_function_t func, jit_value_t value, const char *prefix)
   134  {
   135  	jit_pool_block_t block;
   136  	unsigned int block_size;
   137  	unsigned int posn;
   138  
   139  	/* Bail out if we have insufficient informaition for the dump */
   140  	if(!stream || !func || !(func->builder) || !value)
   141  	{
   142  		return;
   143  	}
   144  
   145  	/* Handle constants and non-local variables */
   146  	if(value->is_constant)
   147  	{
   148  		jit_constant_t const_value;
   149  		char buf[64];
   150  		char *name;
   151  		const_value = jit_value_get_constant(value);
   152  		switch((jit_type_promote_int
   153  					(jit_type_normalize(const_value.type)))->kind)
   154  		{
   155  			case JIT_TYPE_INT:
   156  			{
   157  				if(const_value.un.int_value < 0)
   158  				{
   159  					name = format_integer
   160  						(buf, 1, (jit_ulong)(jit_uint)
   161  							(-(const_value.un.int_value)));
   162  				}
   163  				else
   164  				{
   165  					name = format_integer
   166  						(buf, 0, (jit_ulong)(jit_uint)
   167  							(const_value.un.int_value));
   168  				}
   169  			}
   170  			break;
   171  
   172  			case JIT_TYPE_UINT:
   173  			{
   174  				name = format_integer
   175  					(buf, 0, (jit_ulong)(const_value.un.uint_value));
   176  			}
   177  			break;
   178  
   179  			case JIT_TYPE_LONG:
   180  			{
   181  				if(const_value.un.long_value < 0)
   182  				{
   183  					name = format_integer
   184  						(buf, 1, (jit_ulong)(-(const_value.un.long_value)));
   185  				}
   186  				else
   187  				{
   188  					name = format_integer
   189  						(buf, 0, (jit_ulong)(const_value.un.long_value));
   190  				}
   191  			}
   192  			break;
   193  
   194  			case JIT_TYPE_ULONG:
   195  			{
   196  				name = format_integer(buf, 0, const_value.un.ulong_value);
   197  			}
   198  			break;
   199  
   200  			case JIT_TYPE_FLOAT32:
   201  			{
   202  				jit_snprintf(buf, sizeof(buf), "%f",
   203  							 (double)(const_value.un.float32_value));
   204  				name = buf;
   205  			}
   206  			break;
   207  
   208  			case JIT_TYPE_FLOAT64:
   209  			{
   210  				jit_snprintf(buf, sizeof(buf), "%f",
   211  							 (double)(const_value.un.float64_value));
   212  				name = buf;
   213  			}
   214  			break;
   215  
   216  			case JIT_TYPE_NFLOAT:
   217  			{
   218  				jit_snprintf(buf, sizeof(buf), "%f",
   219  							 (double)(const_value.un.nfloat_value));
   220  				name = buf;
   221  			}
   222  			break;
   223  
   224  			default:
   225  			{
   226  				name = "<unknown-constant>";
   227  			}
   228  			break;
   229  		}
   230  		fputs(name, stream);
   231  		return;
   232  	}
   233  	else if(value->is_local && value->block->func != func)
   234  	{
   235  		/* Accessing a local variable in an outer function frame */
   236  		int scope = 0;
   237  		while(func && func->builder && func != value->block->func)
   238  		{
   239  			++scope;
   240  			func = func->nested_parent;
   241  		}
   242  		fprintf(stream, "{%d}", scope);
   243  		if(!func || !(func->builder))
   244  		{
   245  			return;
   246  		}
   247  	}
   248  
   249  	/* Intuit the prefix if one was not supplied */
   250  	if(!prefix)
   251  	{
   252  		switch(jit_type_normalize(jit_value_get_type(value))->kind)
   253  		{
   254  			case JIT_TYPE_VOID:			prefix = "v"; break;
   255  			case JIT_TYPE_SBYTE:		prefix = "i"; break;
   256  			case JIT_TYPE_UBYTE:		prefix = "i"; break;
   257  			case JIT_TYPE_SHORT:		prefix = "i"; break;
   258  			case JIT_TYPE_USHORT:		prefix = "i"; break;
   259  			case JIT_TYPE_INT:			prefix = "i"; break;
   260  			case JIT_TYPE_UINT:			prefix = "i"; break;
   261  			case JIT_TYPE_LONG:			prefix = "l"; break;
   262  			case JIT_TYPE_ULONG:		prefix = "l"; break;
   263  			case JIT_TYPE_FLOAT32:		prefix = "f"; break;
   264  			case JIT_TYPE_FLOAT64:		prefix = "d"; break;
   265  			case JIT_TYPE_NFLOAT:		prefix = "D"; break;
   266  			case JIT_TYPE_STRUCT:		prefix = "s"; break;
   267  			case JIT_TYPE_UNION:		prefix = "u"; break;
   268  			default:					prefix = "?"; break;
   269  		}
   270  	}
   271  
   272  	/* Get the position of the value within the function's value pool */
   273  	block = func->builder->value_pool.blocks;
   274  	block_size = func->builder->value_pool.elem_size *
   275  				 func->builder->value_pool.elems_per_block;
   276  	posn = 1;
   277  	while(block != 0)
   278  	{
   279  		if(((char *)value) >= block->data &&
   280  		   ((char *)value) < (block->data + block_size))
   281  		{
   282  			posn += (((char *)value) - block->data) /
   283  					func->builder->value_pool.elem_size;
   284  			break;
   285  		}
   286  		posn += func->builder->value_pool.elems_per_block;
   287  		block = block->next;
   288  	}
   289  
   290  	/* Dump the prefix and the position, as the value's final name */
   291  	fprintf(stream, "%s%u", prefix, posn);
   292  }
   293  
   294  /*
   295   * Dump a temporary value, prefixed by its type.
   296   */
   297  static void dump_value(FILE *stream, jit_function_t func,
   298  					   jit_value_t value, int type)
   299  {
   300  	/* Normalize the type, so that it reflects JIT_OPCODE_DEST_xxx values */
   301  	if((type & JIT_OPCODE_SRC1_MASK) != 0)
   302  	{
   303  		type >>= 4;
   304  	}
   305  	if((type & JIT_OPCODE_SRC2_MASK) != 0)
   306  	{
   307  		type >>= 8;
   308  	}
   309  
   310  	/* Dump the value, prefixed appropriately */
   311  	switch(type)
   312  	{
   313  		case JIT_OPCODE_DEST_INT:
   314  		{
   315  			jit_dump_value(stream, func, value, "i");
   316  		}
   317  		break;
   318  
   319  		case JIT_OPCODE_DEST_LONG:
   320  		{
   321  			jit_dump_value(stream, func, value, "l");
   322  		}
   323  		break;
   324  
   325  		case JIT_OPCODE_DEST_FLOAT32:
   326  		{
   327  			jit_dump_value(stream, func, value, "f");
   328  		}
   329  		break;
   330  
   331  		case JIT_OPCODE_DEST_FLOAT64:
   332  		{
   333  			jit_dump_value(stream, func, value, "d");
   334  		}
   335  		break;
   336  
   337  		case JIT_OPCODE_DEST_NFLOAT:
   338  		{
   339  			jit_dump_value(stream, func, value, "D");
   340  		}
   341  		break;
   342  
   343  		case JIT_OPCODE_DEST_ANY:
   344  		{
   345  			/* Intuit the prefix from the value if the type is "any" */
   346  			jit_dump_value(stream, func, value, 0);
   347  		}
   348  		break;
   349  	}
   350  }
   351  
   352  /*@
   353   * @deftypefun void jit_dump_insn (FILE *@var{stream}, jit_function_t @var{func}, jit_insn_t @var{insn})
   354   * Dump the contents of an instruction to a stdio stream.
   355   * @end deftypefun
   356  @*/
   357  void jit_dump_insn(FILE *stream, jit_function_t func, jit_insn_t insn)
   358  {
   359  	const char *name;
   360  	const char *infix_name;
   361  	int opcode, flags;
   362  	jit_nint reg;
   363  
   364  	/* Bail out if we have insufficient information for the dump */
   365  	if(!stream || !func || !insn)
   366  	{
   367  		return;
   368  	}
   369  
   370  	/* Get the opcode details */
   371  	opcode = insn->opcode;
   372  	if(opcode < JIT_OP_NOP || opcode >= JIT_OP_NUM_OPCODES)
   373  	{
   374  		fprintf(stream, "unknown opcode %d\n", opcode);
   375  		return;
   376  	}
   377  	name = jit_opcodes[opcode].name;
   378  	flags = jit_opcodes[opcode].flags;
   379  	infix_name = 0;
   380  
   381  	/* Dump branch, call, or register information */
   382  	if((flags & JIT_OPCODE_IS_BRANCH) != 0)
   383  	{
   384  		if(opcode == JIT_OP_BR)
   385  		{
   386  			fprintf(stream, "goto .L%ld", (long)(jit_insn_get_label(insn)));
   387  			return;
   388  		}
   389  		if(opcode == JIT_OP_CALL_FINALLY || opcode == JIT_OP_CALL_FILTER)
   390  		{
   391  			fprintf(stream, "%s .L%ld", name, (long)(jit_insn_get_label(insn)));
   392  			return;
   393  		}
   394  		fprintf(stream, "if ");
   395  	}
   396  	else if((flags & JIT_OPCODE_IS_CALL) != 0)
   397  	{
   398  		if(insn->value1)
   399  			fprintf(stream, "%s %s", name, (const char *)(insn->value1));
   400  		else
   401  			fprintf(stream, "%s 0x08%lx", name, (long)(jit_nuint)(insn->dest));
   402  		return;
   403  	}
   404  	else if((flags & JIT_OPCODE_IS_CALL_EXTERNAL) != 0)
   405  	{
   406  		if(insn->value1)
   407  			fprintf(stream, "%s %s (0x%08lx)", name,
   408  					(const char *)(insn->value1),
   409  					(long)(jit_nuint)(insn->dest));
   410  		else
   411  			fprintf(stream, "%s 0x08%lx", name,
   412  					(long)(jit_nuint)(insn->dest));
   413  		return;
   414  	}
   415  	else if((flags & JIT_OPCODE_IS_REG) != 0)
   416  	{
   417  		reg = jit_value_get_nint_constant(jit_insn_get_value2(insn));
   418  		fputs(name, stream);
   419  		putc('(', stream);
   420  		jit_dump_value(stream, func, jit_insn_get_value1(insn), 0);
   421  		fputs(", ", stream);
   422  		fputs(jit_reg_name(reg), stream);
   423  		putc(')', stream);
   424  		return;
   425  	}
   426  	else if((flags & JIT_OPCODE_IS_ADDROF_LABEL) != 0)
   427  	{
   428  		dump_value(stream, func, jit_insn_get_dest(insn), flags & JIT_OPCODE_DEST_MASK);
   429  		fprintf(stream, " = ");
   430  		fprintf(stream, "address_of_label .L%ld",
   431  				(long)(jit_insn_get_label(insn)));
   432  		return;
   433  	}
   434  	else if((flags & JIT_OPCODE_IS_JUMP_TABLE) != 0)
   435  	{
   436  		jit_label_t *labels;
   437  		jit_nint num_labels, label;
   438  		labels = (jit_label_t *)jit_value_get_nint_constant(jit_insn_get_value1(insn));
   439  		num_labels = jit_value_get_nint_constant(jit_insn_get_value2(insn));
   440  		fprintf(stream, "%s ", name);
   441  		dump_value(stream, func, jit_insn_get_dest(insn), flags & JIT_OPCODE_DEST_MASK);
   442  		printf(" : {");
   443  		for(label = 0; label < num_labels; label++)
   444  		{
   445  			printf(" .L%ld", (long) labels[label]);
   446  		}
   447  		printf(" }");
   448  		return;
   449  	}
   450  
   451  	/* Output the destination information */
   452  	if((flags & JIT_OPCODE_DEST_MASK) != JIT_OPCODE_DEST_EMPTY &&
   453  	   !jit_insn_dest_is_value(insn))
   454  	{
   455  		dump_value(stream, func, jit_insn_get_dest(insn),
   456  				   flags & JIT_OPCODE_DEST_MASK);
   457  		fprintf(stream, " = ");
   458  	}
   459  
   460  	/* Dump the details of the operation */
   461  	switch(flags & JIT_OPCODE_OPER_MASK)
   462  	{
   463  		case JIT_OPCODE_OPER_ADD:			infix_name = " + ";   break;
   464  		case JIT_OPCODE_OPER_SUB:			infix_name = " - ";   break;
   465  		case JIT_OPCODE_OPER_MUL:			infix_name = " * ";   break;
   466  		case JIT_OPCODE_OPER_DIV:			infix_name = " / ";   break;
   467  		case JIT_OPCODE_OPER_REM:			infix_name = " % ";   break;
   468  		case JIT_OPCODE_OPER_NEG:			infix_name = "-";    break;
   469  		case JIT_OPCODE_OPER_AND:			infix_name = " & ";   break;
   470  		case JIT_OPCODE_OPER_OR:			infix_name = " | ";   break;
   471  		case JIT_OPCODE_OPER_XOR:			infix_name = " ^ ";   break;
   472  		case JIT_OPCODE_OPER_NOT:			infix_name = "~";    break;
   473  		case JIT_OPCODE_OPER_EQ:			infix_name = " == ";  break;
   474  		case JIT_OPCODE_OPER_NE:			infix_name = " != ";  break;
   475  		case JIT_OPCODE_OPER_LT:			infix_name = " < ";   break;
   476  		case JIT_OPCODE_OPER_LE:			infix_name = " <= ";  break;
   477  		case JIT_OPCODE_OPER_GT:			infix_name = " > ";   break;
   478  		case JIT_OPCODE_OPER_GE:			infix_name = " >= ";  break;
   479  		case JIT_OPCODE_OPER_SHL:			infix_name = " << ";  break;
   480  		case JIT_OPCODE_OPER_SHR:			infix_name = " >> ";  break;
   481  		case JIT_OPCODE_OPER_SHR_UN:		infix_name = " >>> "; break;
   482  		case JIT_OPCODE_OPER_COPY:			infix_name = "";      break;
   483  		case JIT_OPCODE_OPER_ADDRESS_OF:	infix_name = "&";    break;
   484  	}
   485  	if(infix_name)
   486  	{
   487  		if((flags & JIT_OPCODE_SRC2_MASK) != 0)
   488  		{
   489  			/* Binary operation with a special operator name */
   490  			dump_value(stream, func, jit_insn_get_value1(insn),
   491  				   	   flags & JIT_OPCODE_SRC1_MASK);
   492  			fputs(infix_name, stream);
   493  			dump_value(stream, func, jit_insn_get_value2(insn),
   494  				   	   flags & JIT_OPCODE_SRC2_MASK);
   495  		}
   496  		else
   497  		{
   498  			/* Unary operation with a special operator name */
   499  			fputs(infix_name, stream);
   500  			dump_value(stream, func, jit_insn_get_value1(insn),
   501  				   	   flags & JIT_OPCODE_SRC1_MASK);
   502  		}
   503  	}
   504  	else
   505  	{
   506  		/* Not a special operator, so use the opcode name */
   507  		if(!jit_strncmp(name, "br_", 3))
   508  		{
   509  			name += 3;
   510  		}
   511  		fputs(name, stream);
   512  		if((flags & (JIT_OPCODE_SRC1_MASK | JIT_OPCODE_SRC2_MASK)) != 0)
   513  		{
   514  			putc('(', stream);
   515  			if(jit_insn_dest_is_value(insn))
   516  			{
   517  				dump_value(stream, func, jit_insn_get_dest(insn),
   518  					   	   flags & JIT_OPCODE_DEST_MASK);
   519  				fputs(", ", stream);
   520  			}
   521  			dump_value(stream, func, jit_insn_get_value1(insn),
   522  				   	   flags & JIT_OPCODE_SRC1_MASK);
   523  			if((flags & JIT_OPCODE_SRC2_MASK) != 0)
   524  			{
   525  				fputs(", ", stream);
   526  				dump_value(stream, func, jit_insn_get_value2(insn),
   527  					   	   flags & JIT_OPCODE_SRC2_MASK);
   528  			}
   529  			putc(')', stream);
   530  		}
   531  	}
   532  
   533  	/* Dump the "then" information on a conditional branch */
   534  	if((flags & JIT_OPCODE_IS_BRANCH) != 0)
   535  	{
   536  		fprintf(stream, " then goto .L%ld", (long)(jit_insn_get_label(insn)));
   537  	}
   538  }
   539  
   540  #if defined(JIT_BACKEND_INTERP)
   541  
   542  /*
   543   * Dump the interpreted bytecode representation of a function.
   544   */
   545  static void dump_interp_code(FILE *stream, void **pc, void **end)
   546  {
   547  	int opcode;
   548  	const jit_opcode_info_t *info;
   549  	while(pc < end)
   550  	{
   551  		/* Fetch the next opcode */
   552  		opcode = (int)(jit_nint)(*pc);
   553  
   554  		/* Dump the address of the opcode */
   555  		fprintf(stream, "\t%08lX: ", (long)(jit_nint)pc);
   556  		++pc;
   557  
   558  		/* Get information about this opcode */
   559  		if(opcode < JIT_OP_NUM_OPCODES)
   560  		{
   561  			info = &(jit_opcodes[opcode]);
   562  		}
   563  		else
   564  		{
   565  			info = &(_jit_interp_opcodes[opcode - JIT_OP_NUM_OPCODES]);
   566  		}
   567  
   568  		/* Dump the name of the opcode */
   569  		fputs(info->name, stream);
   570  
   571  		/* Dump additional parameters from the opcode stream */
   572  		switch(info->flags & JIT_OPCODE_INTERP_ARGS_MASK)
   573  		{
   574  			case JIT_OPCODE_NINT_ARG:
   575  			{
   576  				fprintf(stream, " %ld", (long)(jit_nint)(*pc));
   577  				++pc;
   578  			}
   579  			break;
   580  
   581  			case JIT_OPCODE_NINT_ARG_TWO:
   582  			{
   583  				fprintf(stream, " %ld, %ld",
   584  						(long)(jit_nint)(pc[0]), (long)(jit_nint)(pc[1]));
   585  				pc += 2;
   586  			}
   587  			break;
   588  
   589  			case JIT_OPCODE_CONST_LONG:
   590  			{
   591  				jit_ulong value;
   592  				jit_memcpy(&value, pc, sizeof(jit_ulong));
   593  				pc += (sizeof(jit_ulong) + sizeof(void *) - 1) /
   594  					  sizeof(void *);
   595  				fprintf(stream, " 0x%lX%08lX",
   596  						(long)((value >> 32) & jit_max_uint),
   597  						(long)(value & jit_max_uint));
   598  			}
   599  			break;
   600  
   601  			case JIT_OPCODE_CONST_FLOAT32:
   602  			{
   603  				jit_float32 value;
   604  				jit_memcpy(&value, pc, sizeof(jit_float32));
   605  				pc += (sizeof(jit_float32) + sizeof(void *) - 1) /
   606  					  sizeof(void *);
   607  				fprintf(stream, " %f", (double)value);
   608  			}
   609  			break;
   610  
   611  			case JIT_OPCODE_CONST_FLOAT64:
   612  			{
   613  				jit_float64 value;
   614  				jit_memcpy(&value, pc, sizeof(jit_float64));
   615  				pc += (sizeof(jit_float64) + sizeof(void *) - 1) /
   616  					  sizeof(void *);
   617  				fprintf(stream, " %f", (double)value);
   618  			}
   619  			break;
   620  
   621  			case JIT_OPCODE_CONST_NFLOAT:
   622  			{
   623  				jit_nfloat value;
   624  				jit_memcpy(&value, pc, sizeof(jit_nfloat));
   625  				pc += (sizeof(jit_nfloat) + sizeof(void *) - 1) /
   626  					  sizeof(void *);
   627  				fprintf(stream, " %f", (double)value);
   628  			}
   629  			break;
   630  
   631  			case JIT_OPCODE_CALL_INDIRECT_ARGS:
   632  			{
   633  				fprintf(stream, " %ld", (long)(jit_nint)(pc[1]));
   634  				pc += 2;
   635  			}
   636  			break;
   637  
   638  			default:
   639  			{
   640  				if((info->flags & (JIT_OPCODE_IS_BRANCH |
   641  								   JIT_OPCODE_IS_ADDROF_LABEL)) != 0)
   642  				{
   643  					fprintf(stream, " %08lX",
   644  							(long)(jit_nint)((pc - 1) + (jit_nint)(*pc)));
   645  					++pc;
   646  				}
   647  				else if((info->flags & JIT_OPCODE_IS_CALL) != 0)
   648  				{
   649  					fprintf(stream, " 0x%lX", (long)(jit_nint)(*pc));
   650  					++pc;
   651  				}
   652  				else if((info->flags & JIT_OPCODE_IS_CALL_EXTERNAL) != 0)
   653  				{
   654  					fprintf(stream, " 0x%lX, %ld",
   655  							(long)(jit_nint)(pc[1]), (long)(jit_nint)(pc[2]));
   656  					pc += 3;
   657  				}
   658  				else if((info->flags & JIT_OPCODE_IS_JUMP_TABLE) != 0)
   659  				{
   660  					jit_nint label, num_labels;
   661  					num_labels = (jit_nint)pc[0];
   662  					for(label = 1; label <= num_labels; label++)
   663  					{
   664  						fprintf(stream,	" %lX",
   665  							(long)(jit_nint)pc[label]);
   666  					}
   667  					pc += 1 + num_labels;
   668  				}
   669  			}
   670  			break;
   671  		}
   672  
   673  		/* Terminate the current disassembly line */
   674  		putc('\n', stream);
   675  	}
   676  }
   677  
   678  #else /* !JIT_BACKEND_INTERP */
   679  
   680  /*
   681   * Dump the native object code representation of a function to stream.
   682   */
   683  static void dump_object_code(FILE *stream, void *start, void *end)
   684  {
   685  	char cmdline[BUFSIZ];
   686  	unsigned char *pc = (unsigned char *)start;
   687  	FILE *file;
   688  	int ch;
   689  
   690  #if JIT_WIN32_PLATFORM
   691  	char s_path[BUFSIZ];
   692  	char o_path[BUFSIZ];
   693  	char *tmp_dir = getenv("TMP");
   694  	if(tmp_dir == NULL)
   695  	{
   696  		tmp_dir = getenv("TEMP");
   697  		if(tmp_dir == NULL)
   698  		{
   699  			tmp_dir = "c:/tmp";
   700  		}
   701  	}
   702  	sprintf(s_path, "%s/libjit-dump.s", tmp_dir);
   703  	sprintf(o_path, "%s/libjit-dump.o", tmp_dir);
   704  #else
   705  	const char *s_path = "/tmp/libjit-dump.s";
   706  	const char *o_path = "/tmp/libjit-dump.o";
   707  #endif
   708  
   709  	file = fopen(s_path, "w");
   710  	if(!file)
   711  	{
   712  		return;
   713  	}
   714  	fflush(stream);
   715  	while(pc < (unsigned char *)end)
   716  	{
   717  		fprintf(file, ".byte %d\n", (int)(*pc));
   718  		++pc;
   719  	}
   720  	fclose(file);
   721  	sprintf(cmdline, "as %s -o %s", s_path, o_path);
   722  	system(cmdline);
   723  	sprintf(cmdline, "objdump --adjust-vma=%ld -d %s > %s",
   724  			(long)(jit_nint)start, o_path, s_path);
   725  	system(cmdline);
   726  	file = fopen(s_path, "r");
   727  	if(file)
   728  	{
   729  		while((ch = getc(file)) != EOF)
   730  		{
   731  			putc(ch, stream);
   732  		}
   733  		fclose(file);
   734  	}
   735  	unlink(s_path);
   736  	unlink(o_path);
   737  	putc('\n', stream);
   738  	fflush(stream);
   739  }
   740  
   741  #endif /* !JIT_BACKEND_INTERP */
   742  
   743  /*@
   744   * @deftypefun void jit_dump_function (FILE *@var{stream}, jit_function_t @var{func}, const char *@var{name})
   745   * Dump the three-address instructions within a function to a stream.
   746   * The @var{name} is attached to the output as a friendly label, but
   747   * has no other significance.
   748   *
   749   * If the function has not been compiled yet, then this will dump the
   750   * three address instructions from the build process.  Otherwise it will
   751   * disassemble and dump the compiled native code.
   752   * @end deftypefun
   753  @*/
   754  void jit_dump_function(FILE *stream, jit_function_t func, const char *name)
   755  {
   756  	jit_block_t block;
   757  	jit_insn_iter_t iter;
   758  	jit_insn_t insn;
   759  	jit_type_t signature;
   760  	unsigned int param;
   761  	unsigned int num_params;
   762  	jit_value_t value;
   763  	jit_label_t label;
   764  
   765  	/* Bail out if we don't have sufficient information to dump */
   766  	if(!stream || !func)
   767  	{
   768  		return;
   769  	}
   770  
   771  	/* Output the function header */
   772  	if(name)
   773  		fprintf(stream, "function %s(", name);
   774  	else
   775  		fprintf(stream, "function 0x%08lX(", (long)(jit_nuint)func);
   776  	signature = func->signature;
   777  	num_params = jit_type_num_params(signature);
   778  	if(func->builder)
   779  	{
   780  		value = jit_value_get_struct_pointer(func);
   781  		if(value || func->nested_parent)
   782  		{
   783  			/* We have extra hidden parameters */
   784  			putc('[', stream);
   785  			if(value)
   786  			{
   787  				jit_dump_value(stream, func, value, 0);
   788  				fputs(" : struct_ptr", stream);
   789  				if(func->nested_parent)
   790  				{
   791  					fputs(", ", stream);
   792  				}
   793  			}
   794  			if(func->nested_parent)
   795  			{
   796  				jit_dump_value(stream, func, func->parent_frame, 0);
   797  				fputs(" : parent_frame", stream);
   798  			}
   799  			putc(']', stream);
   800  			if(num_params > 0)
   801  			{
   802  				fputs(", ", stream);
   803  			}
   804  		}
   805  		for(param = 0; param < num_params; ++param)
   806  		{
   807  			if(param != 0)
   808  			{
   809  				fputs(", ", stream);
   810  			}
   811  			value = jit_value_get_param(func, param);
   812  			if(value)
   813  			{
   814  				jit_dump_value(stream, func, value, 0);
   815  			}
   816  			else
   817  			{
   818  				fputs("???", stream);
   819  			}
   820  			fputs(" : ", stream);
   821  			jit_dump_type(stream, jit_type_get_param(signature, param));
   822  		}
   823  	}
   824  	else
   825  	{
   826  		for(param = 0; param < num_params; ++param)
   827  		{
   828  			if(param != 0)
   829  			{
   830  				fputs(", ", stream);
   831  			}
   832  			jit_dump_type(stream, jit_type_get_param(signature, param));
   833  		}
   834  	}
   835  	fprintf(stream, ") : ");
   836  	jit_dump_type(stream, jit_type_get_return(signature));
   837  	putc('\n', stream);
   838  
   839  	/* Should we dump the three address code or the native code? */
   840  	if(func->builder)
   841  	{
   842  		/* Output each of the three address blocks in turn */
   843  		block = 0;
   844  		while((block = jit_block_next(func, block)) != 0)
   845  		{
   846  			/* Output the block's labels, if it has any */
   847  			label = jit_block_get_label(block);
   848  			if(block->label != jit_label_undefined)
   849  			{
   850  				for(;;)
   851  				{
   852  					fprintf(stream, ".L%ld:", (long) label);
   853  					label = jit_block_get_next_label(block, label);
   854  					if(label == jit_label_undefined)
   855  					{
   856  						fprintf(stream, "\n");
   857  						break;
   858  					}
   859  					fprintf(stream, " ");
   860  				}
   861  			}
   862  			else if (block != func->builder->entry_block
   863  				 /*&& _jit_block_get_last(block) != 0*/)
   864  			{
   865  				/* A new block was started, but it doesn't have a label yet */
   866  				fprintf(stream, ".L:\n");
   867  			}
   868  
   869  			/* Dump the instructions in the block */
   870  			jit_insn_iter_init(&iter, block);
   871  			while((insn = jit_insn_iter_next(&iter)) != 0)
   872  			{
   873  				putc('\t', stream);
   874  				jit_dump_insn(stream, func, insn);
   875  				putc('\n', stream);
   876  			}
   877  			if(block->ends_in_dead)
   878  			{
   879  				fputs("\tends_in_dead\n", stream);
   880  			}
   881  		}
   882  	}
   883  	else if(func->is_compiled)
   884  	{
   885  		void *start = func->entry_point;
   886  		void *info = _jit_memory_find_function_info(func->context, start);
   887  		void *end = _jit_memory_get_function_end(func->context, info);
   888  #if defined(JIT_BACKEND_INTERP)
   889  		/* Dump the interpreter's bytecode representation */
   890  		jit_function_interp_t interp;
   891  		interp = (jit_function_interp_t)(func->entry_point);
   892  		fprintf(stream, "\t%08lX: prolog(0x%lX, %d, %d, %d)\n",
   893  				(long)(jit_nint)interp, (long)(jit_nint)func,
   894  				(int)(interp->args_size), (int)(interp->frame_size),
   895  				(int)(interp->working_area));
   896  		dump_interp_code(stream, (void **)(interp + 1), (void **)end);
   897  #else
   898  		dump_object_code(stream, start, end);
   899  #endif
   900  	}
   901  
   902  	/* Output the function footer */
   903  	fprintf(stream, "end\n\n");
   904  	fflush(stream);
   905  }