# ==++==
# 
#   
#    Copyright (c) 2002 Microsoft Corporation.  All rights reserved.
#   
#    The use and distribution terms for this software are contained in the file
#    named license.txt, which can be found in the root of this distribution.
#    By using this software in any fashion, you are agreeing to be bound by the
#    terms of this license.
#   
#    You must not remove this notice, or any other, from this software.
#   
# 
# ==--==
# GENREFOPS.PL
#
# PERL script used to generate the numbering of the reference opcodes
#
#use strict 'vars';
#use strict 'subs';
#use strict 'refs';


my $ret = 0;
my %oneByte;
my %twoByte;
my %controlFlow;
my @singleByteArg;
my %stackbehav;
my %opcodetype;
my %operandtype;
my %opcodes;
my $popstate;
my $pushstate;

$ctrlflowcount = 0;

$count = 0;

my @lowercaseAlphabet = ('a'..'z','0'..'9');
my %upcaseAlphabet = ();

foreach $letter (@lowercaseAlphabet) {
    $j = $letter;
    $j=~tr/a-z/A-Z/;
    $upcaseAlphabet{$letter}=$j;
}

open (OPCODE, "opcode.def") or die "Couldn't open opcode.def: $!\n";
open (OUTPUT, ">OpCodes.cs") or die "Couldn't open OpCodes.cs: $!\n";
open (FCOUTPUT, ">FlowControl.cs") or die "Couldn't open FlowControl.cs: $!\n";
open (SOUTPUT, ">StackBehaviour.cs") or die "Couldn't open StackBehaviour.cs: $!\n";
open (OCOUTPUT, ">OpCodeType.cs") or die "Couldn't open OpCodeType.cs: $!\n";
open (OPOUTPUT, ">OperandType.cs") or die "Couldn't open OprandType.cs: $!\n";


print OUTPUT "/*============================================================\n";
print OUTPUT "**\n";
print OUTPUT "**Class: OpCodes\n";
print OUTPUT "**\n";
print OUTPUT "**Purpose: Exposes all of the il instructions supported by the runtime.\n";
print OUTPUT "**\n";
print OUTPUT "**Copyright (c) Microsoft Corporation.  All rights reserved.\n";
print OUTPUT "**\n";
print OUTPUT "** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND!\n";
print OUTPUT "** See clr\src\inc\opcodegen.pl for more information.**\n";
print OUTPUT "============================================================*/\n";

print OUTPUT "namespace System.Reflection.Emit {\n\n";
print OUTPUT "using System;\n\n";;
print OUTPUT "public class OpCodes {\n\n";;
print OUTPUT "/// <summary>\n";
print OUTPUT "///    <para>\n";
print OUTPUT "///       The IL instruction opcodes supported by the\n";
print OUTPUT "///       runtime. The IL Instruction Specification describes each\n";
print OUTPUT "///       Opcode.\n";
print OUTPUT "///    </para>\n";
print OUTPUT "/// </summary>\n";
print OUTPUT "/// <seealso topic='IL Instruction Set       Specification'/>\n";
print OUTPUT "\n";
print OUTPUT "\tprivate OpCodes() { \n\t}\n\n";


print FCOUTPUT "/*============================================================\n";
print FCOUTPUT "**\n";
print FCOUTPUT "**Class: FlowControl\n";
print FCOUTPUT "**\n";
print FCOUTPUT "**Purpose: Exposes FlowControl Attribute of IL .\n";
print FCOUTPUT "**\n";
print FCOUTPUT "**Copyright (c) Microsoft Corporation.  All rights reserved.\n";
print FCOUTPUT "**\n";
print FCOUTPUT "** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND!\n";
print FCOUTPUT "** See clr\src\inc\opcodegen.pl for more information.**\n";
print FCOUTPUT "============================================================*/\n";

print FCOUTPUT "namespace System.Reflection.Emit {\n\n";
print FCOUTPUT "using System;\n\n";
print FCOUTPUT "public enum FlowControl\n{\n\n";

print SOUTPUT "/*============================================================\n";
print SOUTPUT "**\n";
print SOUTPUT "**Class: StackBehaviour\n";
print SOUTPUT "**\n";
print SOUTPUT "**Purpose: Exposes StackBehaviour Attribute of IL .\n";
print SOUTPUT "**\n";
print SOUTPUT "**Copyright (c) Microsoft Corporation.  All rights reserved.\n";
print SOUTPUT "**\n";
print SOUTPUT "** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND!\n";
print SOUTPUT "** See clr\src\inc\opcodegen.pl for more information.**\n";
print SOUTPUT "============================================================*/\n";

print SOUTPUT "namespace System.Reflection.Emit {\n\n";
print SOUTPUT "using System;\n\n";
print SOUTPUT "public enum StackBehaviour\n{\n\n";

print OCOUTPUT "/*============================================================\n";
print OCOUTPUT "**\n";
print OCOUTPUT "**Class: OpCodeType\n";
print OCOUTPUT "**\n";
print OCOUTPUT "**Purpose: Exposes OpCodeType Attribute of IL .\n";
print OCOUTPUT "**\n";
print OCOUTPUT "**Copyright (c) Microsoft Corporation.  All rights reserved.\n";
print OCOUTPUT "**\n";
print OCOUTPUT "** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND!\n";
print OCOUTPUT "** See clr\src\inc\opcodegen.pl for more information.**\n";
print OCOUTPUT "============================================================*/\n";

print OCOUTPUT "namespace System.Reflection.Emit {\n\n";
print OCOUTPUT "using System;\n\n";
print OCOUTPUT "public enum OpCodeType\n{\n\n";

print OPOUTPUT "/*============================================================\n";
print OPOUTPUT "**\n";
print OPOUTPUT "**Class: OperandType\n";
print OPOUTPUT "**\n";
print OPOUTPUT "**Purpose: Exposes OperandType Attribute of IL .\n";
print OPOUTPUT "**\n";
print OPOUTPUT "**Copyright (c) Microsoft Corporation.  All rights reserved.\n";
print OPOUTPUT "**\n";
print OPOUTPUT "** THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT BY HAND!\n";
print OPOUTPUT "** See clr\src\inc\opcodegen.pl for more information.**\n";
print OPOUTPUT "============================================================*/\n";

print OPOUTPUT "namespace System.Reflection.Emit {\n\n";
print OPOUTPUT "using System;\n\n";
print OPOUTPUT "public enum OperandType\n{\n\n";

while (<OPCODE>)
{
    # Process only OPDEF(....) lines
    if (/OPDEF\(\s*/)
    {
	chop;               # Strip off trailing CR
	s/^OPDEF\(\s*//;    # Strip off "OP("
	s/\)$//;            # Strip off ")" at end
	s/,\s*/,/g;         # Remove whitespace

	# Split the line up into its basic parts
	($enumname, $stringname, $pop, $push, $operand, $type, $size, $s1, $s2, $ctrl) = split(/,/);
	$s1 =~ s/0x//;
	$s1 = hex($s1);
	$s2 =~ s/0x//;
	$s2 = hex($s2);

	if ($size == 0)
	{
		next;
	}

	next if ($enumname =~ /UNUSED/);

	#Remove the prefix
	$enumname=~s/CEE_//g;

	#Convert name to our casing convention
	$enumname=~tr/A-Z/a-z/;
	$enumname=~s/^(.)/\u$1/g;
	$enumname=~s/_(.)/_\u$1/g;


	#Convert pop to our casing convention
	$pop=~tr/A-Z/a-z/;
	$pop=~s/^(.)/\u$1/g;
	$pop=~s/_(.)/_\u$1/g;


	#Convert push to our casing convention
	$push=~tr/A-Z/a-z/;
	$push=~s/^(.)/\u$1/g;
	$push=~s/_(.)/_\u$1/g;


	#Convert operand to our casing convention
	#$operand=~tr/A-Z/a-z/;
	#$operand=~s/^(.)/\u$1/g;
	#$operand=~s/_(.)/_\u$1/g;

	#Remove the I prefix on type
	$type=~s/I//g;

	#Convert Type to our casing convention
	$type=~tr/A-Z/a-z/;
	$type=~s/^(.)/\u$1/g;
	$type=~s/_(.)/_\u$1/g;


	#Convert ctrl to our casing convention
	$ctrl=~tr/A-Z/a-z/;
	$ctrl=~s/^(.)/\u$1/g;
	$ctrl=~s/_(.)/_\u$1/g;




	# Make a list of the flow Control type

	# Make a list of the opcodes and there values
	if ($opcodes{$enumname})
	{

	}
	else
	{
		if ($size == 1)
		{
			$opcodes{$enumname} = $s2;
		}
		else
		{
			$opcodes{$enumname} = ($s2 + 256 * $s1);
		}
	}

	#Make a list of the instructions which only take one-byte arguments
	if ($enumname =~ /^.*_S$/) {
	    #but exclude the deprecated expressions (sometimes spelled "depricated")
	    if (!($enumname=~/^Depr.cated.*/)) {
		my $caseStatement = sprintf("\t\tcase %-20s: \n", $enumname);
		push(@singleByteArg, $caseStatement);
	    }
	}

	#make a list of the control Flow Types
	if ($controlFlow{$ctrl})
	{
		#printf("DUPE Control Flow\n");
	}
	else
	{
		$controlFlow{$ctrl} = $ctrlflowcount;
		$ctrlflowcount++;
	}

	$ctrlflowcount	= 0;
	#make a list of the control Flow Types
	$pop=~s/\+/_/g;
	if ($stackbehav{$pop})
	{
		#printf("DUPE Control Flow\n");
	}
	else
	{
		$stackbehav{$pop} = $ctrlflowcount;
		$ctrlflowcount++;
	}

	#make a list of the control Flow Types
	$push=~s/\+/_/g;
	if ($stackbehav{$push})
	{
		#printf("DUPE Control Flow\n");
	}
	else
	{
		$stackbehav{$push} = $ctrlflowcount;
		$ctrlflowcount++;
	}
	#make a list of operand types
	if ($operandtype{$operand})
	{
		#printf("DUPE Control Flow\n");
	}
	else
	{
		$operandtype{$operand} = $ctrlflowcount;
		$ctrlflowcount++;
	}


	#make a list of opcode types
	if ($opcodetype{$type})
	{
		#printf("DUPE Control Flow\n");
	}
	else
	{
		$opcodetype{$type} = $ctrlflowcount;
		$ctrlflowcount++;
	}

	print "$enumname\n";
	print "$stringname\n";
	if ($stringname eq "arglist")
	{
		print "This is arglist----------\n";
	}
	my $line = sprintf("\tpublic static readonly OpCode %-20s = ", $enumname);
	if ($size == 1)
	{
	    $line .=  sprintf("new OpCode(%s, StackBehaviour.%s, StackBehaviour.%s, OperandType.%s, OpCodeType.%s, %u, (byte)0x%x, (byte)0x%x, FlowControl.%s, " ,  $stringname, $pop, $push, $operand, $type, $size, $s1, $s2, $ctrl);


	    if ($ctrl eq "Return" || $ctrl eq "Branch" || $ctrl eq "Throw" || $stringname eq "\"jmp\"" || $stringname eq "\"jmpi\"")
	    {
	    	$line .=  sprintf("true, ");

	    }
	    else
	    {
	    	$line .=  sprintf("false, ");
	    }

	    $popstate = 0;
	    if($pop eq "Pop0" || $pop eq "Varpop")
	    {
	    	$popstate = 0;
	    }
	    elsif ($pop eq "Pop1" || $pop eq "Popi" || $pop eq "Popref")
	    {
	    	$popstate = $popstate -1;
	    }
	    elsif ($pop eq "Pop1_pop1" || $pop eq "Popi_pop1" || $pop eq "Popi_popi" || $pop eq "Popi_popi8" || $pop eq "Popi_popr4" || $pop eq "Popi_popr8" || $pop eq "Popref_pop1" || $pop eq "Popref_popi")
	    {
	    	$popstate = $popstate -2;
	    }
	    elsif ($pop eq "Popi_popi_popi" || $pop eq "Popref_popi_popi" || $pop eq "Popref_popi_popi8" || $pop eq "Popref_popi_popr4" || $pop eq "Popref_popi_popr8" || $pop eq "Popref_popi_popref")
	    {
	    	$popstate = $popstate -3;
	    }

	    if ($push eq "Push1" || $push eq "Pushi" ||$push eq "Pushi8" ||$push eq "Pushr4" ||$push eq "Pushr8" ||$push eq "Pushref")
	    {
	    	$popstate = $popstate + 1;
	    }
	    elsif($push eq "Push1_push1")
	    {
	    	$popstate = $popstate + 2;
	    }


	    $line .=  sprintf(" $popstate");
	    $line .=  sprintf(");\n\n");
	    if ($oneByte{$s2})
		{
			printf("Error opcode 0x%x  already defined!\n", $s2);
			print "   Old = $oneByte{$s2}";
			print "   New = $line";
			$ret = -1;
	    }
	    $oneByte{$s2} = $line;
	}
	elsif ($size == 2)
	{
	    if ($twoByte{$s2})
		{
			printf("Error opcode 0x%x%x  already defined!\n", $s1, $s2);
			print "   Old = $oneByte{$s2}";
			print "   New = $line";
			$ret = -1;
	    }
	    $line .= sprintf("new OpCode(%s, StackBehaviour.%s, StackBehaviour.%s, OperandType.%s, OpCodeType.%s, %u, (byte)0x%x, (byte)0x%x, FlowControl.%s, " ,  $stringname, $pop, $push, $operand, $type, $size, $s1, $s2, $ctrl);

	    if ($ctrl eq "Return" || $ctrl eq "Branch" || $ctrl eq "Throw" || $stringname eq "\"jmp\"" || $stringname eq "\"jmpi\"")
	    {
	    	$line .=  sprintf("true, ");

	    }
	    else
	    {
	    	$line .=  sprintf("false, ");
	    }

	    $popstate = 0;
	    if($pop eq "Pop0" || $pop eq "Varpop")
	    {
	    	$popstate = 0;
	    }
	    elsif($pop eq "Pop1" || $pop eq "Popi" || $pop eq "Popref")
	    {
	    	$popstate = $popstate -1;
	    }
	    elsif ($pop eq "Pop1_pop1" || $pop eq "Popi_pop1" || $pop eq "Popi_popi" || $pop eq "Popi_popi8" || $pop eq "Popi_popr4" || $pop eq "Popi_popr8" || $pop eq "Popref_pop1" || $pop eq "Popref_popi")
	    {
	    	$popstate = $popstate -2;
	    }
	    elsif ($pop eq "Popi_popi_popi" || $pop eq "Popref_popi_popi" || $pop eq "Popref_popi_popi8" || $pop eq "Popref_popi_popr4" || $pop eq "Popref_popi_popr8" || $pop eq "Popref_popi_popref")
	    {
	    	$popstate = $popstate -3;
	    }

	    if ($push eq "Push1" || $push eq "Pushi" ||$push eq "Pushi8" ||$push eq "Pushr4" ||$push eq "Pushr8" ||$push eq "Pushref")
	    {
	    	$popstate = $popstate + 1;
	    }
	    elsif($push eq "Push1_push1")
	    {
	    	$popstate = $popstate + 2;
	    }

	    $line .=  sprintf(" $popstate");
	    $line .=  sprintf(");\n\n");

	    $twoByte{$s2 + 256 * $s1} = $line;

	}
	else
	{
	    $line .= "\n";
	    push(@deprecated, $line);
	}
	$count++;
    }
}

#Write out the Flow Control class
$ctrlflowcount = 0;
foreach $key (sort {$a cmp $b} keys (%controlFlow))
{
	print FCOUTPUT "\t$key";
	print FCOUTPUT "\t= $ctrlflowcount,\n";
	$ctrlflowcount++;
}

#end the flowcontrol class
print FCOUTPUT "}\n}";

#Write out the StackBehaviour class
$ctrlflowcount = 0;
foreach $key (sort {$a cmp $b} keys (%stackbehav))
{
	print SOUTPUT "\t$key";
	print SOUTPUT "\t= $ctrlflowcount,\n";
	$ctrlflowcount++;
}
#end the StackBehaviour class
print SOUTPUT "}\n}";

$ctrlflowcount = 0;
foreach $key (sort {$a cmp $b} keys (%opcodetype))
{
	print OCOUTPUT "\t$key";
	print OCOUTPUT "\t= $ctrlflowcount,\n";
	$ctrlflowcount++;
}
#end the OpCodeTYpe class
print OCOUTPUT "}\n}";

$ctrlflowcount = 0;
foreach $key (sort {$a cmp $b} keys (%operandtype))
{
	print OPOUTPUT "\t$key";
	print OPOUTPUT "\t= $ctrlflowcount,\n";
	$ctrlflowcount++;
}
#end the OperandType class
print OPOUTPUT "}\n}";


my $opcode;
my $lastOp = -1;
foreach $opcode (sort {$a <=> $b} keys(%oneByte)) {
    printf("***** GAP %d instrs ****\n", $opcode - $lastOp) if ($lastOp + 1 != $opcode && $lastOp > 0);
    print OUTPUT $oneByte{$opcode};
    $lastOp = $opcode;
}

$lastOp = -1;
foreach $opcode (sort {$a <=> $b} keys(%twoByte)) {
    printf("***** GAP %d instrs ****\n", $opcode - $lastOp) if ($lastOp + 1 != $opcode && $lastOp > 0);
    print OUTPUT $twoByte{$opcode};
    $lastOp = $opcode;
}





print OUTPUT "\n\n\tpublic static bool TakesSingleByteArgument(OpCode inst)\n\t{\n";
print OUTPUT"\t\tswitch(inst.m_operand)\n\t\t{\n";
print OUTPUT "\t\t\tcase OperandType.ShortInlineBrTarget :\n";
print OUTPUT "\t\t\tcase OperandType.ShortInlineI :\n";
print OUTPUT "\t\t\tcase OperandType.ShortInlineVar :\n";
print OUTPUT "\t\t\treturn true;\n";
print OUTPUT "\t\t};\n";
print OUTPUT "\t\treturn false;\n";
print OUTPUT "\t}\n";
print OUTPUT "}\n}";
exit($ret);



