#!/usr/local/bin/perl -w


##
## Copyright (c) 2000, Intel Corporation
## All rights reserved.
##
## WARRANTY DISCLAIMER
##
## THESE MATERIALS ARE PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ITS 
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING
## NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THESE
## MATERIALS, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
##
## Intel Corporation is the author of the Materials, and requests that all
## problem reports or change requests be submitted to it directly at
## http://developer.intel.com/opensource.
##

$GenDir = shift(@ARGV);
$EmdbDir = shift(@ARGV);


### This script reads dec_emdb.tab, which holds for each inst_id its format,
### major opcode, and template_role. It creates builder_info.c which contains
### two initialized arrays: 
###    - square_formats[] - the list of formats in each square
###    - square_emdb_lines[] - the list of inst_id's in each square
###    - format_emdb_lines[] - the list of inst_id's in each format
###    - format_extension_masks[] - extension mask from each format
###    - LOG2[] - an array which hold the log2 value foreach number
###               between 0 and 2^11-1
### It also creates builder_info.h which contains the relevant typedefs.

open(EMDB, "$GenDir/dec_ign_emdb.tab") || die "Can't open dec_ign_emdb.tab\n";
open(OUT_C, ">$GenDir/builder_info.c") || die "Can't open builder_info.c\n";
open(OUT_H, ">$GenDir/builder_info.h") || die "Can't open builder_info.h\n";
#if ($ENV{HOSTTYPE} eq "WINNT")
#{
#    open(FORMAT, "$EmdbDir/emdb_formats.txt") ||
#    die "Can't open emdb_formats.txt\n";
#}
#else

{
    open(FORMAT, "$EmdbDir/emdb_formats.txt") ||
    die "Can't open emdb_formats.txt\n";
}

require "EM_perl.h";

$MAX_FORMATS_IN_SQUARE = 1;
$MAX_EMDB_LINES_IN_SQUARE = 1;
$MAX_EMDB_LINES_IN_FORMAT = 1;

### create all 64 initial squares
@template_roles = ("INT", "MEM", "FP", "BR", "LONG");
for ($i = 0; $i < $EM_NUM_OF_MAJOR_OPCODES; $i++)
{
	for ($j = 0; $j <= $#template_roles; $j++)
	{
		$square = sprintf("0x%x_%s", $i, $template_roles[$j]);
		$square =~ tr/[a-z]/[A-Z]/;
		push (@square_enum, $square);
	}
}


### major opcodes 0x3 and 0x6 of BRANCH instructions are ignored

$emdb_lines_in_square{"0X3_BR"} = 1;
$square_emdb_lines{"0X3_BR"} = "EM_IGNOP";

$emdb_lines_in_square{"0X6_BR"} = 1;
$square_emdb_lines{"0X6_BR"} = "EM_IGNOP";


### add each emdb line to calculated square and format
while (<EMDB>)
{
	if (/^EM/)
	{
		/(EM_\w+),(\w+),(\w+),(\w+),/;
		$inst_id = $1;
		$format = $2;
		$major_opcode = $3;
		$template_role[0] = $4;
        $template_role[1] = "";

#		if ($template_role[0] eq "LONG")
#		{
#			$template_role[0] = "INT";
#		}

        
#       if ($template_role[0] eq "BR2")
# 		{
#			$template_role[0] = "BR";
#		}

        if ($template_role[0] eq "ALU")
        {
            $template_role[0] = "INT";
            $template_role[1] = "MEM";
        }


#        if ($inst_id =~ /EM_MOVL_/)   ### movl case
#        {
#            $template_role[0] = "LONG";
#        }

		$tr_no = 0;

        while ($template_role[$tr_no])
        {
			$square = $major_opcode . "_" . $template_role[$tr_no];
			$square =~ tr/[a-z]/[A-Z]/;

			if ($format ne "NONE")
			{
				$index = -1;
				for ($i = 0; $i <= $#square_enum; $i++)
				{
					if ($square eq $square_enum[$i])
					{
						$index = $i;
					}
				}
				if ($index == -1)
				{
					die "$square not known\n";
				}
				
				$format = "EM_FORMAT_" . $format;
				if (!defined $square_formats{$square})
				{
					$square_formats{$square} = $format;
					$formats_in_square{$square} = 1;
				}
				else
				{
					if (!defined $format_emdb_lines{$format})
					{
						$formats_in_square{$square} += 1;
						if ($formats_in_square{$square} > $MAX_FORMATS_IN_SQUARE)
						{
							$MAX_FORMATS_IN_SQUARE = $formats_in_square{$square};
						}
						$square_formats{$square} .= "," . $format;
					}
				}
				
				if (!defined $format_emdb_lines{$format})
				{
					$emdb_lines_in_format{$format} = 1;
					$format_emdb_lines{$format} = $inst_id;
				}
				else
				{
					$format_emdb_lines{$format} .= "," . $inst_id;
					$emdb_lines_in_format{$format} += 1;
					if ($emdb_lines_in_format{$format} > 
						$MAX_EMDB_LINES_IN_FORMAT)
					{
						$MAX_EMDB_LINES_IN_FORMAT = 
							$emdb_lines_in_format{$format};
					}
				}
				
				if (!defined $square_emdb_lines{$square})
				{
					$emdb_lines_in_square{$square} = 1;
					$square_emdb_lines{$square} = $inst_id;
				}
				else
				{
					$emdb_lines_in_square{$square} += 1;
					if ($emdb_lines_in_square{$square} > 
						$MAX_EMDB_LINES_IN_SQUARE)
					{
						$MAX_EMDB_LINES_IN_SQUARE = 
							$emdb_lines_in_square{$square};
					}
					$square_emdb_lines{$square} .= "," . $inst_id;
				}
			}
			$tr_no++;
		}
	}
}


### check that there are no instructions with ignored major opcodes

if ($emdb_lines_in_square{"0X3_BR"} != 1 || $emdb_lines_in_square{"0X6_BR"} != 1)
{
    print STDERR "ERROR: Exist EM_OPROLE_BR instructions with
                  ignored major opcode (0x3 or 0x6) !!!\n";
}


################################################
### Calculate extension bit mask for each format
################################################
push(@format_enum, "EM_FORMAT_NONE");
$mask{"EM_FORMAT_NONE"} = "IEL_CONST64(0, 0)";
$ext{"EM_FORMAT_NONE"} = "{0,0}";
for ($i=1; $i < $MAX_EXTENSION; $i++)
{
	$ext{"EM_FORMAT_NONE"} .= ",{0,0}";
}

while(<FORMAT>)
{
	if (/^(EM_FORMAT_\w+)/)
	{
		@format_line = split(/\s+/, $_);
		$format_name = $1;
		push (@format_enum, $format_name);
	#	$mask{$format_name} = 0;
        $mask_low = 0;
        $mask_high = 0;
		$ext{$format_name} = "";
		for ($i = 1; $i <= $MAX_EXTENSION; $i++)
		{
			$format_line[$i] =~ /(\d+).(\d+)/;
			$pos = $1;
			$size = $2;
			$ext{$format_name} .= "\{$pos,$size\}";
			$ext{$format_name} .= "," unless ($i == $MAX_EXTENSION);
			if ($size != 0)
			{
				$pos -= $EM_PREDICATE_BITS;	 #position relative to the end of qp field	

                if ($pos < 0)   #bit mask contribution in bits 0-5
                {               
                    $mask_low |= ((1 << $size) - 1) << ($pos + $EM_PREDICATE_BITS);
                }   
                else            #bit mask contribution in bits 6-36 
                {
				    $mask_high |= ((1 << $size) - 1) << $pos;
                }
			}
		}

        ###combine low and high masks into U64 integer
        $mask_low |= ($mask_high << $EM_PREDICATE_BITS);
        $mask_high >>= (32 - $EM_PREDICATE_BITS);
        $mask{$format_name} = sprintf("IEL_CONST64(0x%x, 0x%x)", $mask_low, $mask_high);
		
	}
}

########################
### Write builder_info.h
########################

select (OUT_H);
print ("#ifndef _BUILDER_INFO_H\n");
print ("#define _BUILDER_INFO_H\n\n");
print ("#include \"emdb_types.h\"\n\n");
print ("#include \"inst_ids.h\"\n\n");
print ("#include \"inst_ign_ids.h\"\n\n");
print ("#include \"iel.h\"\n\n");

### print constants
print ("#define MAX_FORMATS_IN_SQUARE $MAX_FORMATS_IN_SQUARE\n\n");
if ($MAX_EMDB_LINES_IN_SQUARE > $MAX_EMDB_LINES_IN_FORMAT)
{
	$MAX_EMDB_LINES = $MAX_EMDB_LINES_IN_SQUARE;
	print ("#define MAX_EMDB_LINES $MAX_EMDB_LINES\n\n");
}
else
{
	$MAX_EMDB_LINES = $MAX_EMDB_LINES_IN_FORMAT;
	print ("#define MAX_EMDB_LINES $MAX_EMDB_LINES\n\n");
}
print("#define MAX_NUM_OF_EXT $MAX_EXTENSION\n\n");

### print square enumeration
printf("\n\ntypedef enum {\n");
printf("    EM_SQUARE_FIRST = 0,\n");
for ($i = 0; $i <= $#square_enum; $i++)
{
	if ($i == 0)
	{
		printf("	EM_SQUARE_$square_enum[0] = EM_SQUARE_FIRST,\n");
	}
	else
	{
		printf("	EM_SQUARE_$square_enum[$i],\n");
	}
}
printf("	EM_SQUARE_LAST\n");
print("} Square_t;\n\n");

print("typedef struct Ext_s\n");
print("{\n");
print("     int pos;\n");
print("     int size;\n");
print("} Ext_t;\n\n");

print("typedef Ext_t Ex_t[MAX_NUM_OF_EXT];\n\n");

#print("typedef struct Format_list_s\n");
#print("{\n");
#print("     int num_of_formats;\n");
#print("     Format_t formats[MAX_FORMATS_IN_SQUARE];\n");
#print("} Format_list_t;\n\n");

print("typedef struct Inst_id_list_s\n");
print("{\n");
print("     int num_of_lines;\n");
print("     Inst_id_t inst_ids[MAX_EMDB_LINES];\n");
print("} Inst_id_list_t;\n\n");

print "extern Ex_t format_extensions\[\];\n";
#print "extern Format_list_t square_formats\[\];\n";
print "extern Inst_id_list_t square_emdb_lines\[\];\n";
#print "extern Inst_id_list_t format_emdb_lines\[\];\n";
print "extern U64 format_extension_masks[];\n";
print "extern int LOG2[];\n\n";

print("#endif  /* _BUILDER_INFO_H */\n");

########################
### Write builder_info.c
########################

select(OUT_C);

print ("/***************************************/\n");
print ("/*****          BUILDER INFO       *****/\n");
print ("/***************************************/\n");

print ("\n\n#include \"builder_info.h\"\n");

### Print info arrays 

### format_extensions[]
printf("\n\nEx_t format_extensions[] = {\n");
for ($i = 0; $i <= $#format_enum; $i++) 
{
	printf ("\/\* $format_enum[$i] \*\/ \{$ext{$format_enum[$i]}\}");
	print "," unless ($i == $#format_enum);
	print "\n";
}
print "};\n";

### square_formats[]
#printf("\n\nFormat_list_t square_formats[] = {\n");
#for ($i = 0; $i <= $#square_enum; $i++) 
#{
#	if (defined $square_formats{$square_enum[$i]})
#	{
#		$formats_line = $square_formats{$square_enum[$i]};
#		for ($j = $formats_in_square{$square_enum[$i]}; 
#			 $j < $MAX_FORMATS_IN_SQUARE; $j++)
#		{
#			$formats_line .= "," . "EM_FORMAT_NONE";
#		}
#	}
#	else
#	{
#		$formats_in_square{$square_enum[$i]} = 0;
#		$formats_line = "EM_FORMAT_NONE";
#		for ($j = 1; $j < $MAX_FORMATS_IN_SQUARE; $j++)
#		{
#			$formats_line .= "," . "EM_FORMAT_NONE";
#		}
#	}
#	print "\/\* $square_enum[$i] \*\/ \{$formats_in_square{$square_enum[$i]}, \{$formats_line\}\}";
#	print "," unless ($i == $#square_enum);
#	print "\n";
#}
#print "};\n";

### square_emdb_lines[]
printf("\n\nInst_id_list_t square_emdb_lines[] = {\n");
for ($i = 0; $i <= $#square_enum; $i++) 
{
	if (defined $square_emdb_lines{$square_enum[$i]})
	{
		$emdb_line = $square_emdb_lines{$square_enum[$i]};
		for ($j = $emdb_lines_in_square{$square_enum[$i]}; 
			 $j < $MAX_EMDB_LINES; $j++)
		{
			$emdb_line .= "," . "EM_INST_NONE";
		}
	}
	else
	{
		$emdb_lines_in_square{$square_enum[$i]} = 0;
		$emdb_line = "EM_INST_NONE";
		for ($j = 1; $j < $MAX_EMDB_LINES; $j++)
		{
			$emdb_line .= "," . "EM_INST_NONE";
		}
	}
	print "\/\* $square_enum[$i] \*\/ \{$emdb_lines_in_square{$square_enum[$i]}, \{$emdb_line\}\}";
	print "," unless ($i == $#square_enum);
	print "\n";
}
print "};\n";

### format_extension_masks[]
printf("\n\nU64 format_extension_masks[] = {\n");
for ($i = 0; $i <= $#format_enum; $i++) 
{
	printf ("\/\* $format_enum[$i] \*\/ $mask{$format_enum[$i]}");
	print "," unless ($i == $#format_enum);
	print "\n";
}
print "};\n";

### format_emdb_lines[]
#printf("\n\nInst_id_list_t format_emdb_lines[] = {\n");
#for ($i = 0; $i <= $#format_enum; $i++) 
#{
#	$emdb_line = $format_emdb_lines{$format_enum[$i]};
#	if (!defined $emdb_lines_in_format{$format_enum[$i]})
#	{
#		$emdb_lines_in_format{$format_enum[$i]} = 0;
#		$emdb_line = "EM_INST_NONE";
#		for ($j = 1; $j < $MAX_EMDB_LINES; $j++)
#		{
#			$emdb_line .= "," . "EM_INST_NONE";
#		}
#	}
#	else
#	{
#		for ($j = $emdb_lines_in_format{$format_enum[$i]}; 
#			 $j < $MAX_EMDB_LINES; $j++)
#		{
#			$emdb_line .= "," . "EM_INST_NONE";
#		}
#	}
#	print "\/\* $format_enum[$i] \*\/ \{$emdb_lines_in_format{$format_enum[$i]}, \{$emdb_line\}\}";
#	print "," unless ($i == $#format_enum);
#	print "\n";
#}
#print "};\n";

### LOG2[]
printf("\n\nint LOG2[] = {\n");
print "     -1,\n";  # LOG2[0] == -1
for ($i = 0; $i <= 10; $i++) 
{
	print "     ";
	for ($j = 1; $j <= 2 ** $i; $j++)
	{
		print $i;
		print "," unless ($i == 10 && $j == 2 ** $i);
	}
	print "\n";
}
print "};\n";