#!/usr/bin/tcl
#
# This script makes modifications to the vdbe.c source file which reduce
# the amount of stack space required by the sqlite3VdbeExec() routine.
#
# The modifications performed by this script are optional.  The vdbe.c
# source file will compile correctly with and without the modifications
# performed by this script.  And all routines within vdbe.c will compute
# the same result.  The modifications made by this script merely help
# the C compiler to generate code for sqlite3VdbeExec() that uses less
# stack space.
#
# Script usage:
#
#          mv vdbe.c vdbe.c.template
#          tclsh vdbe-compress.tcl $CFLAGS <vdbe.c.template >vdbe.c
#
# Modifications made:
#
# All modifications are within the sqlite3VdbeExec() function.  The
# modifications seek to reduce the amount of stack space allocated by
# this routine by moving local variable declarations out of individual
# opcode implementations and into a single large union.  The union contains
# a separate structure for each opcode and that structure contains the
# local variables used by that opcode.  In this way, the total amount
# of stack space required by sqlite3VdbeExec() is reduced from the
# sum of all local variables to the maximum of the local variable space
# required for any single opcode.
#
# In order to be recognized by this script, local variables must appear
# on the first line after the open curly-brace that begins a new opcode
# implementation.  Local variables must not have initializers, though they
# may be commented.
#
# The union definition is inserted in place of a special marker comment
# in the preamble to the sqlite3VdbeExec() implementation.
#
#############################################################################
#
set beforeUnion {}   ;# C code before union
set unionDef {}      ;# C code of the union
set afterUnion {}    ;# C code after the union
set sCtr 0           ;# Context counter

# If the SQLITE_SMALL_STACK compile-time option is missing, then
# this transformation becomes a no-op.
#
if {![regexp {SQLITE_SMALL_STACK} $argv]} {
  while {![eof stdin]} {
    puts [gets stdin]
  }
  exit
}

# Read program text up to the spot where the union should be
# inserted.
#
while {![eof stdin]} {
  set line [gets stdin]
  if {[regexp {INSERT STACK UNION HERE} $line]} break
  append beforeUnion $line\n
}

# Process the remaining text.  Build up the union definition as we go.
#
set vlist {}
set seenDecl 0
set namechars {abcdefghijklmnopqrstuvwxyz}
set nnc [string length $namechars]
while {![eof stdin]} {
  set line [gets stdin]
  if {[regexp "^case (OP_\\w+): \173" $line all operator]} {
    append afterUnion $line\n
    set vlist {}
    while {![eof stdin]} {
      set line [gets stdin]
      if {[regexp {^ +(const )?\w+ \**(\w+)(\[.*\])?;} $line \
           all constKeyword vname notused1]} {
        if {!$seenDecl} {
          set sname {}
          append sname [string index $namechars [expr {$sCtr/$nnc}]]
          append sname [string index $namechars [expr {$sCtr%$nnc}]]
          incr sCtr
          append unionDef "    struct ${operator}_stack_vars \173\n"
          append afterUnion \
             "#if 0  /* local variables moved into u.$sname */\n"
          set seenDecl 1
        }
        append unionDef "    $line\n"
        append afterUnion $line\n
        lappend vlist $vname
      } elseif {[regexp {^#(if|endif)} $line] && [llength $vlist]>0} {
        append unionDef "$line\n"
        append afterUnion $line\n
      } else {
        break
      }
    }
    if {$seenDecl} {
      append unionDef   "    \175 $sname;\n"
      append afterUnion "#endif /* local variables moved into u.$sname */\n"
    }
    set seenDecl 0
  }
  if {[regexp "^\175" $line]} {
    append afterUnion $line\n
    set vlist {}
  } elseif {[llength $vlist]>0} {
    append line " "
    foreach v $vlist {
      regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line
      regsub -all "(\[^a-zA-Z0-9>.\])${v}(\\W)" $line "\\1u.$sname.$v\\2" line
    }
    append afterUnion [string trimright $line]\n
  } elseif {$line=="" && [eof stdin]} {
    # no-op
  } else {
    append afterUnion $line\n
  }
}

# Output the resulting text.
#
puts -nonewline $beforeUnion
puts "  /********************************************************************"
puts "  ** Automatically generated code"
puts "  **"
puts "  ** The following union is automatically generated by the"
puts "  ** vdbe-compress.tcl script.  The purpose of this union is to"
puts "  ** reduce the amount of stack space required by this function."
puts "  ** See comments in the vdbe-compress.tcl script for details."
puts "  */"
puts "  union vdbeExecUnion \173"
puts -nonewline $unionDef
puts "  \175 u;"
puts "  /* End automatically generated code"
puts "  ********************************************************************/"
puts -nonewline $afterUnion
