xref: /sqlite-3.40.0/tool/mkopcodeh.tcl (revision 064390b2)
1b15393b8Sdrh#!/usr/bin/tclsh
2b15393b8Sdrh#
3b15393b8Sdrh# Generate the file opcodes.h.
4b15393b8Sdrh#
5b15393b8Sdrh# This TCL script scans a concatenation of the parse.h output file from the
6b15393b8Sdrh# parser and the vdbe.c source file in order to generate the opcodes numbers
7b15393b8Sdrh# for all opcodes.
8b15393b8Sdrh#
9b15393b8Sdrh# The lines of the vdbe.c that we are interested in are of the form:
10b15393b8Sdrh#
11b15393b8Sdrh#       case OP_aaaa:      /* same as TK_bbbbb */
12b15393b8Sdrh#
13b15393b8Sdrh# The TK_ comment is optional.  If it is present, then the value assigned to
14b15393b8Sdrh# the OP_ is the same as the TK_ value.  If missing, the OP_ value is assigned
15b15393b8Sdrh# a small integer that is different from every other OP_ value.
16b15393b8Sdrh#
17b15393b8Sdrh# We go to the trouble of making some OP_ values the same as TK_ values
18b15393b8Sdrh# as an optimization.  During parsing, things like expression operators
19b15393b8Sdrh# are coded with TK_ values such as TK_ADD, TK_DIVIDE, and so forth.  Later
20b15393b8Sdrh# during code generation, we need to generate corresponding opcodes like
21b15393b8Sdrh# OP_Add and OP_Divide.  By making TK_ADD==OP_Add and TK_DIVIDE==OP_Divide,
22b15393b8Sdrh# code to translate from one to the other is avoided.  This makes the
237cc84c2cSdrh# code generator smaller and faster.
24b15393b8Sdrh#
25b15393b8Sdrh# This script also scans for lines of the form:
26b15393b8Sdrh#
27758784d1Smistachkin#       case OP_aaaa:       /* jump, in1, in2, in3, out2, out3 */
28b15393b8Sdrh#
29b15393b8Sdrh# When such comments are found on an opcode, it means that certain
30b15393b8Sdrh# properties apply to that opcode.  Set corresponding flags using the
31b15393b8Sdrh# OPFLG_INITIALIZER macro.
32b15393b8Sdrh#
33b15393b8Sdrh
34b15393b8Sdrhset in stdin
35b15393b8Sdrhset currentOp {}
36758784d1Smistachkinset prevName {}
37b15393b8Sdrhset nOp 0
38758784d1Smistachkinset nGroup 0
39b15393b8Sdrhwhile {![eof $in]} {
40b15393b8Sdrh  set line [gets $in]
41b15393b8Sdrh
42b15393b8Sdrh  # Remember the TK_ values from the parse.h file.
43b15393b8Sdrh  # NB:  The "TK_" prefix stands for "ToKen", not the graphical Tk toolkit
44b15393b8Sdrh  # commonly associated with TCL.
45b15393b8Sdrh  #
46b15393b8Sdrh  if {[regexp {^#define TK_} $line]} {
47b15393b8Sdrh    set tk([lindex $line 1]) [lindex $line 2]
48b15393b8Sdrh    continue
49b15393b8Sdrh  }
50b15393b8Sdrh
51b15393b8Sdrh  # Find "/* Opcode: " lines in the vdbe.c file.  Each one introduces
52b15393b8Sdrh  # a new opcode.  Remember which parameters are used.
53b15393b8Sdrh  #
54b15393b8Sdrh  if {[regexp {^.. Opcode: } $line]} {
55b15393b8Sdrh    set currentOp OP_[lindex $line 2]
56b15393b8Sdrh    set m 0
57b15393b8Sdrh    foreach term $line {
58b15393b8Sdrh      switch $term {
59b15393b8Sdrh        P1 {incr m 1}
60b15393b8Sdrh        P2 {incr m 2}
61b15393b8Sdrh        P3 {incr m 4}
62b15393b8Sdrh        P4 {incr m 8}
63b15393b8Sdrh        P5 {incr m 16}
64b15393b8Sdrh      }
65b15393b8Sdrh    }
66b15393b8Sdrh    set paramused($currentOp) $m
67b15393b8Sdrh  }
68b15393b8Sdrh
69b15393b8Sdrh  # Find "** Synopsis: " lines that follow Opcode:
70b15393b8Sdrh  #
71b15393b8Sdrh  if {[regexp {^.. Synopsis: (.*)} $line all x] && $currentOp!=""} {
72b15393b8Sdrh    set synopsis($currentOp) [string trim $x]
73b15393b8Sdrh  }
74b15393b8Sdrh
75b15393b8Sdrh  # Scan for "case OP_aaaa:" lines in the vdbe.c file
76b15393b8Sdrh  #
77b15393b8Sdrh  if {[regexp {^case OP_} $line]} {
78b15393b8Sdrh    set line [split $line]
79b15393b8Sdrh    set name [string trim [lindex $line 1] :]
804031bafaSdrh    if {$name=="OP_Abortable"} continue;  # put OP_Abortable last
81b15393b8Sdrh    set op($name) -1
82758784d1Smistachkin    set group($name) 0
83b15393b8Sdrh    set jump($name) 0
84b15393b8Sdrh    set in1($name) 0
85b15393b8Sdrh    set in2($name) 0
86b15393b8Sdrh    set in3($name) 0
87b15393b8Sdrh    set out2($name) 0
88ed94af5eSdrh    set out3($name) 0
89b15393b8Sdrh    for {set i 3} {$i<[llength $line]-1} {incr i} {
90b15393b8Sdrh       switch [string trim [lindex $line $i] ,] {
91b15393b8Sdrh         same {
92b15393b8Sdrh           incr i
93b15393b8Sdrh           if {[lindex $line $i]=="as"} {
94b15393b8Sdrh             incr i
95b15393b8Sdrh             set sym [string trim [lindex $line $i] ,]
96b15393b8Sdrh             set val $tk($sym)
97b15393b8Sdrh             set op($name) $val
98b15393b8Sdrh             set used($val) 1
99b15393b8Sdrh             set sameas($val) $sym
100b15393b8Sdrh             set def($val) $name
101b15393b8Sdrh           }
102b15393b8Sdrh         }
103758784d1Smistachkin         group {set group($name) 1}
104b15393b8Sdrh         jump  {set jump($name) 1}
105b15393b8Sdrh         in1   {set in1($name) 1}
106b15393b8Sdrh         in2   {set in2($name) 1}
107b15393b8Sdrh         in3   {set in3($name) 1}
108b15393b8Sdrh         out2  {set out2($name) 1}
109b15393b8Sdrh         out3  {set out3($name) 1}
110b15393b8Sdrh       }
111b15393b8Sdrh    }
112758784d1Smistachkin    if {$group($name)} {
113758784d1Smistachkin      set newGroup 0
114758784d1Smistachkin      if {[info exists groups($nGroup)]} {
115758784d1Smistachkin        if {$prevName=="" || !$group($prevName)} {
116758784d1Smistachkin          set newGroup 1
117758784d1Smistachkin        }
118758784d1Smistachkin      }
119758784d1Smistachkin      lappend groups($nGroup) $name
120758784d1Smistachkin      if {$newGroup} {incr nGroup}
121758784d1Smistachkin    } else {
122758784d1Smistachkin      if {$prevName!="" && $group($prevName)} {
123758784d1Smistachkin        incr nGroup
124758784d1Smistachkin      }
125758784d1Smistachkin    }
126b15393b8Sdrh    set order($nOp) $name
127758784d1Smistachkin    set prevName $name
128b15393b8Sdrh    incr nOp
129b15393b8Sdrh  }
130b15393b8Sdrh}
131b15393b8Sdrh
132b15393b8Sdrh# Assign numbers to all opcodes and output the result.
133b15393b8Sdrh#
134b15393b8Sdrhputs "/* Automatically generated.  Do not edit */"
135b15393b8Sdrhputs "/* See the tool/mkopcodeh.tcl script for details */"
1364031bafaSdrhforeach name {OP_Noop OP_Explain OP_Abortable} {
137ed94af5eSdrh  set jump($name) 0
138ed94af5eSdrh  set in1($name) 0
139ed94af5eSdrh  set in2($name) 0
140ed94af5eSdrh  set in3($name) 0
141ed94af5eSdrh  set out2($name) 0
142ed94af5eSdrh  set out3($name) 0
143ed94af5eSdrh  set op($name) -1
144ed94af5eSdrh  set order($nOp) $name
145b15393b8Sdrh  incr nOp
146ed94af5eSdrh}
147b15393b8Sdrh
148e74ca517Sdrh# The following are the opcodes that receive special processing in the
149e74ca517Sdrh# resolveP2Values() routine.  Update this list whenever new cases are
150e74ca517Sdrh# added to the pOp->opcode switch within resolveP2Values().
151b15393b8Sdrh#
152b15393b8Sdrhset rp2v_ops {
153b15393b8Sdrh  OP_Transaction
154b15393b8Sdrh  OP_AutoCommit
155b15393b8Sdrh  OP_Savepoint
156b15393b8Sdrh  OP_Checkpoint
157b15393b8Sdrh  OP_Vacuum
158b15393b8Sdrh  OP_JournalMode
159b15393b8Sdrh  OP_VUpdate
160b15393b8Sdrh  OP_VFilter
161*064390b2Sdrh  OP_Init
162b15393b8Sdrh}
163b15393b8Sdrh
164e74ca517Sdrh# Assign the smallest values to opcodes that are processed by resolveP2Values()
165b15393b8Sdrh# to make code generation for the switch() statement smaller and faster.
166b15393b8Sdrh#
167ed94af5eSdrhset cnt -1
168b15393b8Sdrhfor {set i 0} {$i<$nOp} {incr i} {
169b15393b8Sdrh  set name $order($i)
170b15393b8Sdrh  if {[lsearch $rp2v_ops $name]>=0} {
171b15393b8Sdrh    incr cnt
172b15393b8Sdrh    while {[info exists used($cnt)]} {incr cnt}
173b15393b8Sdrh    set op($name) $cnt
174b15393b8Sdrh    set used($cnt) 1
175b15393b8Sdrh    set def($cnt) $name
176b15393b8Sdrh  }
177b15393b8Sdrh}
178e74ca517Sdrhset mxCase1 $cnt
179b15393b8Sdrh
1807cc84c2cSdrh# Assign the next group of values to JUMP opcodes
1817cc84c2cSdrh#
1827cc84c2cSdrhfor {set i 0} {$i<$nOp} {incr i} {
1837cc84c2cSdrh  set name $order($i)
1847cc84c2cSdrh  if {$op($name)>=0} continue
1857cc84c2cSdrh  if {!$jump($name)} continue
1867cc84c2cSdrh  incr cnt
1877cc84c2cSdrh  while {[info exists used($cnt)]} {incr cnt}
1887cc84c2cSdrh  set op($name) $cnt
1897cc84c2cSdrh  set used($cnt) 1
1907cc84c2cSdrh  set def($cnt) $name
1917cc84c2cSdrh}
1927cc84c2cSdrh
1937cc84c2cSdrh# Find the numeric value for the largest JUMP opcode
1947cc84c2cSdrh#
1957cc84c2cSdrhset mxJump -1
1967cc84c2cSdrhfor {set i 0} {$i<$nOp} {incr i} {
1977cc84c2cSdrh  set name $order($i)
1987cc84c2cSdrh  if {$jump($name) && $op($name)>$mxJump} {set mxJump $op($name)}
1997cc84c2cSdrh}
2007cc84c2cSdrh
2017cc84c2cSdrh
202758784d1Smistachkin# Generate the numeric values for all remaining opcodes, while
203758784d1Smistachkin# preserving any groupings of opcodes (i.e. those that must be
204758784d1Smistachkin# together).
205b15393b8Sdrh#
206758784d1Smistachkinfor {set g 0} {$g<$nGroup} {incr g} {
207758784d1Smistachkin  set gLen [llength $groups($g)]
208758784d1Smistachkin  set ok 0; set start -1
209f9ac1ab1Smistachkin  set seek $cnt
210758784d1Smistachkin  while {!$ok} {
211f9ac1ab1Smistachkin    incr seek
212758784d1Smistachkin    while {[info exists used($seek)]} {incr seek}
213758784d1Smistachkin    set ok 1; set start $seek
214758784d1Smistachkin    for {set j 0} {$j<$gLen} {incr j} {
215758784d1Smistachkin      incr seek
216758784d1Smistachkin      if {[info exists used($seek)]} {
217758784d1Smistachkin        set ok 0; break
218758784d1Smistachkin      }
219758784d1Smistachkin    }
220758784d1Smistachkin  }
221758784d1Smistachkin  if {$ok} {
222758784d1Smistachkin    set next $start
223758784d1Smistachkin    for {set j 0} {$j<$gLen} {incr j} {
224758784d1Smistachkin      set name [lindex $groups($g) $j]
225758784d1Smistachkin      if {$op($name)>=0} continue
226758784d1Smistachkin      set op($name) $next
227758784d1Smistachkin      set used($next) 1
228758784d1Smistachkin      set def($next) $name
229758784d1Smistachkin      incr next
230758784d1Smistachkin    }
231758784d1Smistachkin  } else {
232758784d1Smistachkin    error "cannot find opcodes for group: $groups($g)"
233758784d1Smistachkin  }
234758784d1Smistachkin}
235758784d1Smistachkin
236b15393b8Sdrhfor {set i 0} {$i<$nOp} {incr i} {
237b15393b8Sdrh  set name $order($i)
238b15393b8Sdrh  if {$op($name)<0} {
239b15393b8Sdrh    incr cnt
240b15393b8Sdrh    while {[info exists used($cnt)]} {incr cnt}
241b15393b8Sdrh    set op($name) $cnt
242b15393b8Sdrh    set used($cnt) 1
243b15393b8Sdrh    set def($cnt) $name
244b15393b8Sdrh  }
245b15393b8Sdrh}
2460e6b8308Sdan
2470e6b8308Sdanset max [lindex [lsort -decr -integer [array names used]] 0]
2480e6b8308Sdanfor {set i 0} {$i<=$max} {incr i} {
249b15393b8Sdrh  if {![info exists used($i)]} {
250b15393b8Sdrh    set def($i) "OP_NotUsed_$i"
251b15393b8Sdrh  }
2520e6b8308Sdan  if {$i>$max} {set max $i}
253b15393b8Sdrh  set name $def($i)
254b15393b8Sdrh  puts -nonewline [format {#define %-16s %3d} $name $i]
255b15393b8Sdrh  set com {}
256758784d1Smistachkin  if {[info exists jump($name)] && $jump($name)} {
257a0286051Sdrh    lappend com "jump"
258a0286051Sdrh  }
259b15393b8Sdrh  if {[info exists sameas($i)]} {
260a0286051Sdrh    lappend com "same as $sameas($i)"
261b15393b8Sdrh  }
262b15393b8Sdrh  if {[info exists synopsis($name)]} {
263a0286051Sdrh    lappend com "synopsis: $synopsis($name)"
264b15393b8Sdrh  }
265a0286051Sdrh  if {[llength $com]} {
266a0286051Sdrh    puts -nonewline [format " /* %-42s */" [join $com {, }]]
267b15393b8Sdrh  }
268b15393b8Sdrh  puts ""
269b15393b8Sdrh}
270b15393b8Sdrh
2710e6b8308Sdanif {$max>255} {
2720e6b8308Sdan  error "More than 255 opcodes - VdbeOp.opcode is of type u8!"
2730e6b8308Sdan}
2740e6b8308Sdan
275b15393b8Sdrh# Generate the bitvectors:
276b15393b8Sdrh#
277b15393b8Sdrhset bv(0) 0
278ed94af5eSdrhfor {set i 0} {$i<=$max} {incr i} {
279ed94af5eSdrh  set x 0
2800e6b8308Sdan  set name $def($i)
2810e6b8308Sdan  if {[string match OP_NotUsed* $name]==0} {
282ed94af5eSdrh    if {$jump($name)}  {incr x 1}
283ed94af5eSdrh    if {$in1($name)}   {incr x 2}
284ed94af5eSdrh    if {$in2($name)}   {incr x 4}
285ed94af5eSdrh    if {$in3($name)}   {incr x 8}
286ed94af5eSdrh    if {$out2($name)}  {incr x 16}
287ed94af5eSdrh    if {$out3($name)}  {incr x 32}
2880e6b8308Sdan  }
289ed94af5eSdrh  set bv($i) $x
290b15393b8Sdrh}
291b15393b8Sdrhputs ""
292b15393b8Sdrhputs "/* Properties such as \"out2\" or \"jump\" that are specified in"
293b15393b8Sdrhputs "** comments following the \"case\" for each opcode in the vdbe.c"
294b15393b8Sdrhputs "** are encoded into bitvectors as follows:"
295b15393b8Sdrhputs "*/"
296ed94af5eSdrhputs "#define OPFLG_JUMP        0x01  /* jump:  P2 holds jmp target */"
297ed94af5eSdrhputs "#define OPFLG_IN1         0x02  /* in1:   P1 is an input */"
298ed94af5eSdrhputs "#define OPFLG_IN2         0x04  /* in2:   P2 is an input */"
299ed94af5eSdrhputs "#define OPFLG_IN3         0x08  /* in3:   P3 is an input */"
300ed94af5eSdrhputs "#define OPFLG_OUT2        0x10  /* out2:  P2 is an output */"
301ed94af5eSdrhputs "#define OPFLG_OUT3        0x20  /* out3:  P3 is an output */"
302b15393b8Sdrhputs "#define OPFLG_INITIALIZER \173\\"
303b15393b8Sdrhfor {set i 0} {$i<=$max} {incr i} {
304b15393b8Sdrh  if {$i%8==0} {
305b15393b8Sdrh    puts -nonewline [format "/* %3d */" $i]
306b15393b8Sdrh  }
307b15393b8Sdrh  puts -nonewline [format " 0x%02x," $bv($i)]
308b15393b8Sdrh  if {$i%8==7} {
309b15393b8Sdrh    puts "\\"
310b15393b8Sdrh  }
311b15393b8Sdrh}
312b15393b8Sdrhputs "\175"
3137cc84c2cSdrhputs ""
314e74ca517Sdrhputs "/* The resolve3P2Values() routine is able to run faster if it knows"
3157cc84c2cSdrhputs "** the value of the largest JUMP opcode.  The smaller the maximum"
3167cc84c2cSdrhputs "** JUMP opcode the better, so the mkopcodeh.tcl script that"
3177cc84c2cSdrhputs "** generated this include file strives to group all JUMP opcodes"
3187cc84c2cSdrhputs "** together near the beginning of the list."
3197cc84c2cSdrhputs "*/"
320c310db39Sdrhputs "#define SQLITE_MX_JUMP_OPCODE  $mxJump  /* Maximum JUMP opcode */"
321