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, 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 prevName {} 37set nOp 0 38set nGroup 0 39while {![eof $in]} { 40 set line [gets $in] 41 42 # Remember the TK_ values from the parse.h file. 43 # NB: The "TK_" prefix stands for "ToKen", not the graphical Tk toolkit 44 # commonly associated with TCL. 45 # 46 if {[regexp {^#define TK_} $line]} { 47 set tk([lindex $line 1]) [lindex $line 2] 48 continue 49 } 50 51 # Find "/* Opcode: " lines in the vdbe.c file. Each one introduces 52 # a new opcode. Remember which parameters are used. 53 # 54 if {[regexp {^.. Opcode: } $line]} { 55 set currentOp OP_[lindex $line 2] 56 set m 0 57 foreach term $line { 58 switch $term { 59 P1 {incr m 1} 60 P2 {incr m 2} 61 P3 {incr m 4} 62 P4 {incr m 8} 63 P5 {incr m 16} 64 } 65 } 66 set paramused($currentOp) $m 67 } 68 69 # Find "** Synopsis: " lines that follow Opcode: 70 # 71 if {[regexp {^.. Synopsis: (.*)} $line all x] && $currentOp!=""} { 72 set synopsis($currentOp) [string trim $x] 73 } 74 75 # Scan for "case OP_aaaa:" lines in the vdbe.c file 76 # 77 if {[regexp {^case OP_} $line]} { 78 set line [split $line] 79 set name [string trim [lindex $line 1] :] 80 if {$name=="OP_Abortable"} continue; # put OP_Abortable last 81 set op($name) -1 82 set group($name) 0 83 set jump($name) 0 84 set in1($name) 0 85 set in2($name) 0 86 set in3($name) 0 87 set out2($name) 0 88 set out3($name) 0 89 for {set i 3} {$i<[llength $line]-1} {incr i} { 90 switch [string trim [lindex $line $i] ,] { 91 same { 92 incr i 93 if {[lindex $line $i]=="as"} { 94 incr i 95 set sym [string trim [lindex $line $i] ,] 96 set val $tk($sym) 97 set op($name) $val 98 set used($val) 1 99 set sameas($val) $sym 100 set def($val) $name 101 } 102 } 103 group {set group($name) 1} 104 jump {set jump($name) 1} 105 in1 {set in1($name) 1} 106 in2 {set in2($name) 1} 107 in3 {set in3($name) 1} 108 out2 {set out2($name) 1} 109 out3 {set out3($name) 1} 110 } 111 } 112 if {$group($name)} { 113 set newGroup 0 114 if {[info exists groups($nGroup)]} { 115 if {$prevName=="" || !$group($prevName)} { 116 set newGroup 1 117 } 118 } 119 lappend groups($nGroup) $name 120 if {$newGroup} {incr nGroup} 121 } else { 122 if {$prevName!="" && $group($prevName)} { 123 incr nGroup 124 } 125 } 126 set order($nOp) $name 127 set prevName $name 128 incr nOp 129 } 130} 131 132# Assign numbers to all opcodes and output the result. 133# 134puts "/* Automatically generated. Do not edit */" 135puts "/* See the tool/mkopcodeh.tcl script for details */" 136foreach name {OP_Noop OP_Explain OP_Abortable} { 137 set jump($name) 0 138 set in1($name) 0 139 set in2($name) 0 140 set in3($name) 0 141 set out2($name) 0 142 set out3($name) 0 143 set op($name) -1 144 set order($nOp) $name 145 incr nOp 146} 147 148# The following are the opcodes that are processed by resolveP2Values() 149# 150set rp2v_ops { 151 OP_Transaction 152 OP_AutoCommit 153 OP_Savepoint 154 OP_Checkpoint 155 OP_Vacuum 156 OP_JournalMode 157 OP_VUpdate 158 OP_VFilter 159 OP_Next 160 OP_NextIfOpen 161 OP_SorterNext 162 OP_Prev 163 OP_PrevIfOpen 164} 165 166# Assign small values to opcodes that are processed by resolveP2Values() 167# to make code generation for the switch() statement smaller and faster. 168# 169set cnt -1 170for {set i 0} {$i<$nOp} {incr i} { 171 set name $order($i) 172 if {[lsearch $rp2v_ops $name]>=0} { 173 incr cnt 174 while {[info exists used($cnt)]} {incr cnt} 175 set op($name) $cnt 176 set used($cnt) 1 177 set def($cnt) $name 178 } 179} 180 181# Assign the next group of values to JUMP opcodes 182# 183for {set i 0} {$i<$nOp} {incr i} { 184 set name $order($i) 185 if {$op($name)>=0} continue 186 if {!$jump($name)} continue 187 incr cnt 188 while {[info exists used($cnt)]} {incr cnt} 189 set op($name) $cnt 190 set used($cnt) 1 191 set def($cnt) $name 192} 193 194# Find the numeric value for the largest JUMP opcode 195# 196set mxJump -1 197for {set i 0} {$i<$nOp} {incr i} { 198 set name $order($i) 199 if {$jump($name) && $op($name)>$mxJump} {set mxJump $op($name)} 200} 201 202 203# Generate the numeric values for all remaining opcodes, while 204# preserving any groupings of opcodes (i.e. those that must be 205# together). 206# 207for {set g 0} {$g<$nGroup} {incr g} { 208 set gLen [llength $groups($g)] 209 set ok 0; set start -1 210 while {!$ok} { 211 set seek $cnt; incr seek 212 while {[info exists used($seek)]} {incr seek} 213 set ok 1; set start $seek 214 for {set j 0} {$j<$gLen} {incr j} { 215 incr seek 216 if {[info exists used($seek)]} { 217 set ok 0; break 218 } 219 } 220 } 221 if {$ok} { 222 set next $start 223 for {set j 0} {$j<$gLen} {incr j} { 224 set name [lindex $groups($g) $j] 225 if {$op($name)>=0} continue 226 set op($name) $next 227 set used($next) 1 228 set def($next) $name 229 incr next 230 } 231 } else { 232 error "cannot find opcodes for group: $groups($g)" 233 } 234} 235 236for {set i 0} {$i<$nOp} {incr i} { 237 set name $order($i) 238 if {$op($name)<0} { 239 incr cnt 240 while {[info exists used($cnt)]} {incr cnt} 241 set op($name) $cnt 242 set used($cnt) 1 243 set def($cnt) $name 244 } 245} 246 247set max [lindex [lsort -decr -integer [array names used]] 0] 248for {set i 0} {$i<=$max} {incr i} { 249 if {![info exists used($i)]} { 250 set def($i) "OP_NotUsed_$i" 251 } 252 if {$i>$max} {set max $i} 253 set name $def($i) 254 puts -nonewline [format {#define %-16s %3d} $name $i] 255 set com {} 256 if {[info exists jump($name)] && $jump($name)} { 257 lappend com "jump" 258 } 259 if {[info exists sameas($i)]} { 260 lappend com "same as $sameas($i)" 261 } 262 if {[info exists synopsis($name)]} { 263 lappend com "synopsis: $synopsis($name)" 264 } 265 if {[llength $com]} { 266 puts -nonewline [format " /* %-42s */" [join $com {, }]] 267 } 268 puts "" 269} 270 271if {$max>255} { 272 error "More than 255 opcodes - VdbeOp.opcode is of type u8!" 273} 274 275# Generate the bitvectors: 276# 277set bv(0) 0 278for {set i 0} {$i<=$max} {incr i} { 279 set x 0 280 set name $def($i) 281 if {[string match OP_NotUsed* $name]==0} { 282 if {$jump($name)} {incr x 1} 283 if {$in1($name)} {incr x 2} 284 if {$in2($name)} {incr x 4} 285 if {$in3($name)} {incr x 8} 286 if {$out2($name)} {incr x 16} 287 if {$out3($name)} {incr x 32} 288 } 289 set bv($i) $x 290} 291puts "" 292puts "/* Properties such as \"out2\" or \"jump\" that are specified in" 293puts "** comments following the \"case\" for each opcode in the vdbe.c" 294puts "** are encoded into bitvectors as follows:" 295puts "*/" 296puts "#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */" 297puts "#define OPFLG_IN1 0x02 /* in1: P1 is an input */" 298puts "#define OPFLG_IN2 0x04 /* in2: P2 is an input */" 299puts "#define OPFLG_IN3 0x08 /* in3: P3 is an input */" 300puts "#define OPFLG_OUT2 0x10 /* out2: P2 is an output */" 301puts "#define OPFLG_OUT3 0x20 /* out3: P3 is an output */" 302puts "#define OPFLG_INITIALIZER \173\\" 303for {set i 0} {$i<=$max} {incr i} { 304 if {$i%8==0} { 305 puts -nonewline [format "/* %3d */" $i] 306 } 307 puts -nonewline [format " 0x%02x," $bv($i)] 308 if {$i%8==7} { 309 puts "\\" 310 } 311} 312puts "\175" 313puts "" 314puts "/* The sqlite3P2Values() routine is able to run faster if it knows" 315puts "** the value of the largest JUMP opcode. The smaller the maximum" 316puts "** JUMP opcode the better, so the mkopcodeh.tcl script that" 317puts "** generated this include file strives to group all JUMP opcodes" 318puts "** together near the beginning of the list." 319puts "*/" 320puts "#define SQLITE_MX_JUMP_OPCODE $mxJump /* Maximum JUMP opcode */" 321