#! /usr/bin/env tclsh

set opcodesIn {
#define INST_DONE			0
#define INST_PUSH1			1
#define INST_PUSH4			2
#define INST_POP			3
#define INST_DUP			4
#define INST_CONCAT1			5
#define INST_INVOKE_STK1		6
#define INST_INVOKE_STK4		7
#define INST_EVAL_STK			8
#define INST_EXPR_STK			9

/* Opcodes 10 to 23 */
#define INST_LOAD_SCALAR1		10
#define INST_LOAD_SCALAR4		11
#define INST_LOAD_SCALAR_STK		12
#define INST_LOAD_ARRAY1		13
#define INST_LOAD_ARRAY4		14
#define INST_LOAD_ARRAY_STK		15
#define INST_LOAD_STK			16
#define INST_STORE_SCALAR1		17
#define INST_STORE_SCALAR4		18
#define INST_STORE_SCALAR_STK		19
#define INST_STORE_ARRAY1		20
#define INST_STORE_ARRAY4		21
#define INST_STORE_ARRAY_STK		22
#define INST_STORE_STK			23

/* Opcodes 24 to 33 */
#define INST_INCR_SCALAR1		24
#define INST_INCR_SCALAR_STK		25
#define INST_INCR_ARRAY1		26
#define INST_INCR_ARRAY_STK		27
#define INST_INCR_STK			28
#define INST_INCR_SCALAR1_IMM		29
#define INST_INCR_SCALAR_STK_IMM	30
#define INST_INCR_ARRAY1_IMM		31
#define INST_INCR_ARRAY_STK_IMM		32
#define INST_INCR_STK_IMM		33

/* Opcodes 34 to 39 */
#define INST_JUMP1			34
#define INST_JUMP4			35
#define INST_JUMP_TRUE1			36
#define INST_JUMP_TRUE4			37
#define INST_JUMP_FALSE1		38
#define INST_JUMP_FALSE4		39

/* Opcodes 40 to 64 */
#define INST_LOR			40
#define INST_LAND			41
#define INST_BITOR			42
#define INST_BITXOR			43
#define INST_BITAND			44
#define INST_EQ				45
#define INST_NEQ			46
#define INST_LT				47
#define INST_GT				48
#define INST_LE				49
#define INST_GE				50
#define INST_LSHIFT			51
#define INST_RSHIFT			52
#define INST_ADD			53
#define INST_SUB			54
#define INST_MULT			55
#define INST_DIV			56
#define INST_MOD			57
#define INST_UPLUS			58
#define INST_UMINUS			59
#define INST_BITNOT			60
#define INST_LNOT			61
#define INST_CALL_BUILTIN_FUNC1		62
#define INST_CALL_FUNC1			63
#define INST_TRY_CVT_TO_NUMERIC		64

/* Opcodes 65 to 66 */
#define INST_BREAK			65
#define INST_CONTINUE			66

/* Opcodes 67 to 68 */
#define INST_FOREACH_START4		67
#define INST_FOREACH_STEP4		68

/* Opcodes 69 to 72 */
#define INST_BEGIN_CATCH4		69
#define INST_END_CATCH			70
#define INST_PUSH_RESULT		71
#define INST_PUSH_RETURN_CODE		72

/* Opcodes 73 to 78 */
#define INST_STR_EQ			73
#define INST_STR_NEQ			74
#define INST_STR_CMP			75
#define INST_STR_LEN			76
#define INST_STR_INDEX			77
#define INST_STR_MATCH			78

/* Opcodes 78 to 81 */
#define INST_LIST			79
#define INST_LIST_INDEX			80
#define INST_LIST_LENGTH		81

/* Opcodes 82 to 87 */
#define INST_APPEND_SCALAR1		82
#define INST_APPEND_SCALAR4		83
#define INST_APPEND_ARRAY1		84
#define INST_APPEND_ARRAY4		85
#define INST_APPEND_ARRAY_STK		86
#define INST_APPEND_STK			87

/* Opcodes 88 to 93 */
#define INST_LAPPEND_SCALAR1		88
#define INST_LAPPEND_SCALAR4		89
#define INST_LAPPEND_ARRAY1		90
#define INST_LAPPEND_ARRAY4		91
#define INST_LAPPEND_ARRAY_STK		92
#define INST_LAPPEND_STK		93

/* TIP #22 - LINDEX operator with flat arg list */

#define INST_LIST_INDEX_MULTI		94

/*
 * TIP #33 - 'lset' command. Code gen also required a Forth-like
 *	     OVER operation.
 */

#define INST_OVER			95
#define INST_LSET_LIST			96
#define INST_LSET_FLAT			97

/* TIP#90 - 'return' command. */

#define INST_RETURN_IMM			98

/* TIP#123 - exponentiation operator. */

#define INST_EXPON			99

/* TIP #157 - {*} ... (word expansion) language syntax support. */

#define INST_EXPAND_START		100
#define INST_EXPAND_STKTOP		101
#define INST_INVOKE_EXPANDED		102

/*
 * TIP #57 - 'lassign' command. Code generation requires immediate
 *	     LINDEX and LRANGE operators.
 */

#define INST_LIST_INDEX_IMM		103
#define INST_LIST_RANGE_IMM		104

#define INST_START_CMD			105

#define INST_LIST_IN			106
#define INST_LIST_NOT_IN		107

#define INST_PUSH_RETURN_OPTIONS	108
#define INST_RETURN_STK			109

/*
 * Dictionary (TIP#111) related commands.
 */

#define INST_DICT_GET			110
#define INST_DICT_SET			111
#define INST_DICT_UNSET			112
#define INST_DICT_INCR_IMM		113
#define INST_DICT_APPEND		114
#define INST_DICT_LAPPEND		115
#define INST_DICT_FIRST			116
#define INST_DICT_NEXT			117
#define INST_DICT_DONE			118
#define INST_DICT_UPDATE_START		119
#define INST_DICT_UPDATE_END		120

/*
 * Instruction to support jumps defined by tables (instead of the classic
 * [switch] technique of chained comparisons).
 */

#define INST_JUMP_TABLE			121

/*
 * Instructions to support compilation of global, variable, upvar and
 * [namespace upvar].
 */

#define INST_UPVAR			122
#define INST_NSUPVAR			123
#define INST_VARIABLE			124

/* Instruction to support compiling syntax error to bytecode */

#define INST_SYNTAX			125

/* Instruction to reverse N items on top of stack */

#define INST_REVERSE			126
}

foreach line [split $opcodesIn \n] {
    lassign $line start name num
    if {$start eq "#define"} {
	set names($num) $name
    }
}

proc stats {fname order {sense {decreasing}}} {
    global names

    #
    # Get the raw data from the file passed as argument
    #

    set f [open $fname]
    set raw [read $f]
    close $f

    set omit {};#{6 102}
    foreach line [split $raw \n] {
	lassign $line inst time count
	if {[string is integer -strict $inst] && ($inst ni $omit)} {
	    incr times($inst) $time
	    incr counts($inst) $count
	    incr totaltime $time
	    incr totalcount $count
	}
    }

    foreach inst [lsort -integer -increasing [array names times]] {
	lappend stats [list \
			   $inst \
			   [format %5.2f [expr {double($times($inst)*100)/$totaltime}]] \
			   [format %8.2f [expr {double($times($inst))/$counts($inst)}]]\
			   [format "%5.2f" [expr {double($counts($inst)*100)/$totalcount}]]\
			   [format %10i $counts($inst)]\
			   $names($inst)]
    }

    set stats [lsort -real -$sense -index $order $stats]

    puts "[info patchlevel] time: $totaltime, count: $totalcount"
    puts ----------------------------------------------------
    puts "op         %T       avgT         %ops         Nops"
    foreach line $stats {
	puts [join $line \t]
    }
}


set orders {inst time avg - count}
lassign $argv fname order
if {$order ni $orders} {
    set order inst
}
if {$order eq "inst"} {
    set sense increasing
} else {
    set sense decreasing
}

stats $fname [lsearch $orders $order] $sense
