1#!/usr/bin/tclsh 2# 3# Generate the file opcodes.h. 4# 5# This TCL script scans a concatenation of the parse.h output file from the 6# parser and the vdbe.c source file in order to generate the opcodes numbers 7# for all opcodes. 8# 9# The lines of the vdbe.c that we are interested in are of the form: 10# 11# case OP_aaaa: /* same as TK_bbbbb */ 12# 13# The TK_ comment is optional. If it is present, then the value assigned to 14# the OP_ is the same as the TK_ value. If missing, the OP_ value is assigned 15# a small integer that is different from every other OP_ value. 16# 17# We go to the trouble of making some OP_ values the same as TK_ values 18# as an optimization. During parsing, things like expression operators 19# are coded with TK_ values such as TK_ADD, TK_DIVIDE, and so forth. Later 20# during code generation, we need to generate corresponding opcodes like 21# OP_Add and OP_Divide. By making TK_ADD==OP_Add and TK_DIVIDE==OP_Divide, 22# code to translate from one to the other is avoided. This makes the 23# code generator smaller and faster. 24# 25# This script also scans for lines of the form: 26# 27# case OP_aaaa: /* jump, in1, in2, in3, out2-prerelease, out3 */ 28# 29# When such comments are found on an opcode, it means that certain 30# properties apply to that opcode. Set corresponding flags using the 31# OPFLG_INITIALIZER macro. 32# 33 34set in stdin 35set currentOp {} 36set nOp 0 37while {![eof $in]} { 38 set line [gets $in] 39 40 # Remember the TK_ values from the parse.h file. 41 # NB: The "TK_" prefix stands for "ToKen", not the graphical Tk toolkit 42 # commonly associated with TCL. 43 # 44 if {[regexp {^#define TK_} $line]} { 45 set tk([lindex $line 1]) [lindex $line 2] 46 continue 47 } 48 49 # Find "/* Opcode: " lines in the vdbe.c file. Each one introduces 50 # a new opcode. Remember which parameters are used. 51 # 52 if {[regexp {^.. Opcode: } $line]} { 53 set currentOp OP_[lindex $line 2] 54 set m 0 55 foreach term $line { 56 switch $term { 57 P1 {incr m 1} 58 P2 {incr m 2} 59 P3 {incr m 4} 60 P4 {incr m 8} 61 P5 {incr m 16} 62 } 63 } 64 set paramused($currentOp) $m 65 } 66 67 # Find "** Synopsis: " lines that follow Opcode: 68 # 69 if {[regexp {^.. Synopsis: (.*)} $line all x] && $currentOp!=""} { 70 set synopsis($currentOp) [string trim $x] 71 } 72 73 # Scan for "case OP_aaaa:" lines in the vdbe.c file 74 # 75 if {[regexp {^case OP_} $line]} { 76 set line [split $line] 77 set name [string trim [lindex $line 1] :] 78 set op($name) -1 79 set jump($name) 0 80 set in1($name) 0 81 set in2($name) 0 82 set in3($name) 0 83 set out2($name) 0 84 set out3($name) 0 85 for {set i 3} {$i<[llength $line]-1} {incr i} { 86 switch [string trim [lindex $line $i] ,] { 87 same { 88 incr i 89 if {[lindex $line $i]=="as"} { 90 incr i 91 set sym [string trim [lindex $line $i] ,] 92 set val $tk($sym) 93 set op($name) $val 94 set used($val) 1 95 set sameas($val) $sym 96 set def($val) $name 97 } 98 } 99 jump {set jump($name) 1} 100 in1 {set in1($name) 1} 101 in2 {set in2($name) 1} 102 in3 {set in3($name) 1} 103 out2 {set out2($name) 1} 104 out3 {set out3($name) 1} 105 } 106 } 107 set order($nOp) $name 108 incr nOp 109 } 110} 111 112# Assign numbers to all opcodes and output the result. 113# 114puts "/* Automatically generated. Do not edit */" 115puts "/* See the tool/mkopcodeh.tcl script for details */" 116foreach name {OP_Noop OP_Explain} { 117 set jump($name) 0 118 set in1($name) 0 119 set in2($name) 0 120 set in3($name) 0 121 set out2($name) 0 122 set out3($name) 0 123 set op($name) -1 124 set order($nOp) $name 125 incr nOp 126} 127 128# The following are the opcodes that are processed by resolveP2Values() 129# 130set rp2v_ops { 131 OP_Transaction 132 OP_AutoCommit 133 OP_Savepoint 134 OP_Checkpoint 135 OP_Vacuum 136 OP_JournalMode 137 OP_VUpdate 138 OP_VFilter 139 OP_Next 140 OP_NextIfOpen 141 OP_SorterNext 142 OP_Prev 143 OP_PrevIfOpen 144} 145 146# Assign small values to opcodes that are processed by resolveP2Values() 147# to make code generation for the switch() statement smaller and faster. 148# 149set cnt -1 150for {set i 0} {$i<$nOp} {incr i} { 151 set name $order($i) 152 if {[lsearch $rp2v_ops $name]>=0} { 153 incr cnt 154 while {[info exists used($cnt)]} {incr cnt} 155 set op($name) $cnt 156 set used($cnt) 1 157 set def($cnt) $name 158 } 159} 160 161# Assign the next group of values to JUMP opcodes 162# 163for {set i 0} {$i<$nOp} {incr i} { 164 set name $order($i) 165 if {$op($name)>=0} continue 166 if {!$jump($name)} continue 167 incr cnt 168 while {[info exists used($cnt)]} {incr cnt} 169 set op($name) $cnt 170 set used($cnt) 1 171 set def($cnt) $name 172} 173 174# Find the numeric value for the largest JUMP opcode 175# 176set mxJump -1 177for {set i 0} {$i<$nOp} {incr i} { 178 set name $order($i) 179 if {$jump($name) && $op($name)>$mxJump} {set mxJump $op($name)} 180} 181 182 183# Generate the numeric values for all remaining opcodes 184# 185for {set i 0} {$i<$nOp} {incr i} { 186 set name $order($i) 187 if {$op($name)<0} { 188 incr cnt 189 while {[info exists used($cnt)]} {incr cnt} 190 set op($name) $cnt 191 set used($cnt) 1 192 set def($cnt) $name 193 } 194} 195 196set max [lindex [lsort -decr -integer [array names used]] 0] 197for {set i 0} {$i<=$max} {incr i} { 198 if {![info exists used($i)]} { 199 set def($i) "OP_NotUsed_$i" 200 } 201 if {$i>$max} {set max $i} 202 set name $def($i) 203 puts -nonewline [format {#define %-16s %3d} $name $i] 204 set com {} 205 if {$jump($name)} { 206 lappend com "jump" 207 } 208 if {[info exists sameas($i)]} { 209 lappend com "same as $sameas($i)" 210 } 211 if {[info exists synopsis($name)]} { 212 lappend com "synopsis: $synopsis($name)" 213 } 214 if {[llength $com]} { 215 puts -nonewline [format " /* %-42s */" [join $com {, }]] 216 } 217 puts "" 218} 219 220if {$max>255} { 221 error "More than 255 opcodes - VdbeOp.opcode is of type u8!" 222} 223 224# Generate the bitvectors: 225# 226set bv(0) 0 227for {set i 0} {$i<=$max} {incr i} { 228 set x 0 229 set name $def($i) 230 if {[string match OP_NotUsed* $name]==0} { 231 if {$jump($name)} {incr x 1} 232 if {$in1($name)} {incr x 2} 233 if {$in2($name)} {incr x 4} 234 if {$in3($name)} {incr x 8} 235 if {$out2($name)} {incr x 16} 236 if {$out3($name)} {incr x 32} 237 } 238 set bv($i) $x 239} 240puts "" 241puts "/* Properties such as \"out2\" or \"jump\" that are specified in" 242puts "** comments following the \"case\" for each opcode in the vdbe.c" 243puts "** are encoded into bitvectors as follows:" 244puts "*/" 245puts "#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */" 246puts "#define OPFLG_IN1 0x02 /* in1: P1 is an input */" 247puts "#define OPFLG_IN2 0x04 /* in2: P2 is an input */" 248puts "#define OPFLG_IN3 0x08 /* in3: P3 is an input */" 249puts "#define OPFLG_OUT2 0x10 /* out2: P2 is an output */" 250puts "#define OPFLG_OUT3 0x20 /* out3: P3 is an output */" 251puts "#define OPFLG_INITIALIZER \173\\" 252for {set i 0} {$i<=$max} {incr i} { 253 if {$i%8==0} { 254 puts -nonewline [format "/* %3d */" $i] 255 } 256 puts -nonewline [format " 0x%02x," $bv($i)] 257 if {$i%8==7} { 258 puts "\\" 259 } 260} 261puts "\175" 262puts "" 263puts "/* The sqlite3P2Values() routine is able to run faster if it knows" 264puts "** the value of the largest JUMP opcode. The smaller the maximum" 265puts "** JUMP opcode the better, so the mkopcodeh.tcl script that" 266puts "** generated this include file strives to group all JUMP opcodes" 267puts "** together near the beginning of the list." 268puts "*/" 269puts "#define SQLITE_MX_JUMP_OPCODE $mxJump /* Maximum JUMP opcode */" 270