mkopcodeh.tcl 9.2 KB

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