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 run (infinitesimally) faster and more importantly it makes 24# the library footprint smaller. 25# 26# This script also scans for lines of the form: 27# 28# case OP_aaaa: /* jump, in1, in2, in3, out2-prerelease, out3 */ 29# 30# When such comments are found on an opcode, it means that certain 31# properties apply to that opcode. Set corresponding flags using the 32# OPFLG_INITIALIZER macro. 33# 34 35set in stdin 36set currentOp {} 37set nOp 0 38while {![eof $in]} { 39 set line [gets $in] 40 41 # Remember the TK_ values from the parse.h file. 42 # NB: The "TK_" prefix stands for "ToKen", not the graphical Tk toolkit 43 # commonly associated with TCL. 44 # 45 if {[regexp {^#define TK_} $line]} { 46 set tk([lindex $line 1]) [lindex $line 2] 47 continue 48 } 49 50 # Find "/* Opcode: " lines in the vdbe.c file. Each one introduces 51 # a new opcode. Remember which parameters are used. 52 # 53 if {[regexp {^.. Opcode: } $line]} { 54 set currentOp OP_[lindex $line 2] 55 set m 0 56 foreach term $line { 57 switch $term { 58 P1 {incr m 1} 59 P2 {incr m 2} 60 P3 {incr m 4} 61 P4 {incr m 8} 62 P5 {incr m 16} 63 } 64 } 65 set paramused($currentOp) $m 66 } 67 68 # Find "** Synopsis: " lines that follow Opcode: 69 # 70 if {[regexp {^.. Synopsis: (.*)} $line all x] && $currentOp!=""} { 71 set synopsis($currentOp) [string trim $x] 72 } 73 74 # Scan for "case OP_aaaa:" lines in the vdbe.c file 75 # 76 if {[regexp {^case OP_} $line]} { 77 set line [split $line] 78 set name [string trim [lindex $line 1] :] 79 set op($name) -1 80 set jump($name) 0 81 set in1($name) 0 82 set in2($name) 0 83 set in3($name) 0 84 set out2($name) 0 85 set out3($name) 0 86 for {set i 3} {$i<[llength $line]-1} {incr i} { 87 switch [string trim [lindex $line $i] ,] { 88 same { 89 incr i 90 if {[lindex $line $i]=="as"} { 91 incr i 92 set sym [string trim [lindex $line $i] ,] 93 set val $tk($sym) 94 set op($name) $val 95 set used($val) 1 96 set sameas($val) $sym 97 set def($val) $name 98 } 99 } 100 jump {set jump($name) 1} 101 in1 {set in1($name) 1} 102 in2 {set in2($name) 1} 103 in3 {set in3($name) 1} 104 out2 {set out2($name) 1} 105 out3 {set out3($name) 1} 106 } 107 } 108 set order($nOp) $name 109 incr nOp 110 } 111} 112 113# Assign numbers to all opcodes and output the result. 114# 115puts "/* Automatically generated. Do not edit */" 116puts "/* See the tool/mkopcodeh.tcl script for details */" 117foreach name {OP_Noop OP_Explain} { 118 set jump($name) 0 119 set in1($name) 0 120 set in2($name) 0 121 set in3($name) 0 122 set out2($name) 0 123 set out3($name) 0 124 set op($name) -1 125 set order($nOp) $name 126 incr nOp 127} 128 129# The following are the opcodes that are processed by resolveP2Values() 130# 131set rp2v_ops { 132 OP_Transaction 133 OP_AutoCommit 134 OP_Savepoint 135 OP_Checkpoint 136 OP_Vacuum 137 OP_JournalMode 138 OP_VUpdate 139 OP_VFilter 140 OP_Next 141 OP_NextIfOpen 142 OP_SorterNext 143 OP_Prev 144 OP_PrevIfOpen 145} 146 147# Assign small values to opcodes that are processed by resolveP2Values() 148# to make code generation for the switch() statement smaller and faster. 149# 150set cnt -1 151for {set i 0} {$i<$nOp} {incr i} { 152 set name $order($i) 153 if {[lsearch $rp2v_ops $name]>=0} { 154 incr cnt 155 while {[info exists used($cnt)]} {incr cnt} 156 set op($name) $cnt 157 set used($cnt) 1 158 set def($cnt) $name 159 } 160} 161 162# Generate the numeric values for remaining opcodes 163# 164for {set i 0} {$i<$nOp} {incr i} { 165 set name $order($i) 166 if {$op($name)<0} { 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} 174set max $cnt 175for {set i 0} {$i<$nOp} {incr i} { 176 if {![info exists used($i)]} { 177 set def($i) "OP_NotUsed_$i" 178 } 179 set name $def($i) 180 puts -nonewline [format {#define %-16s %3d} $name $i] 181 set com {} 182 if {[info exists sameas($i)]} { 183 set com "same as $sameas($i)" 184 } 185 if {[info exists synopsis($name)]} { 186 set x $synopsis($name) 187 if {$com==""} { 188 set com "synopsis: $x" 189 } else { 190 append com ", synopsis: $x" 191 } 192 } 193 if {$com!=""} { 194 puts -nonewline [format " /* %-42s */" $com] 195 } 196 puts "" 197} 198 199# Generate the bitvectors: 200# 201set bv(0) 0 202for {set i 0} {$i<=$max} {incr i} { 203 set name $def($i) 204 set x 0 205 if {$jump($name)} {incr x 1} 206 if {$in1($name)} {incr x 2} 207 if {$in2($name)} {incr x 4} 208 if {$in3($name)} {incr x 8} 209 if {$out2($name)} {incr x 16} 210 if {$out3($name)} {incr x 32} 211 set bv($i) $x 212} 213puts "" 214puts "/* Properties such as \"out2\" or \"jump\" that are specified in" 215puts "** comments following the \"case\" for each opcode in the vdbe.c" 216puts "** are encoded into bitvectors as follows:" 217puts "*/" 218puts "#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */" 219puts "#define OPFLG_IN1 0x02 /* in1: P1 is an input */" 220puts "#define OPFLG_IN2 0x04 /* in2: P2 is an input */" 221puts "#define OPFLG_IN3 0x08 /* in3: P3 is an input */" 222puts "#define OPFLG_OUT2 0x10 /* out2: P2 is an output */" 223puts "#define OPFLG_OUT3 0x20 /* out3: P3 is an output */" 224puts "#define OPFLG_INITIALIZER \173\\" 225for {set i 0} {$i<=$max} {incr i} { 226 if {$i%8==0} { 227 puts -nonewline [format "/* %3d */" $i] 228 } 229 puts -nonewline [format " 0x%02x," $bv($i)] 230 if {$i%8==7} { 231 puts "\\" 232 } 233} 234puts "\175" 235