xref: /aosp_15_r20/external/starlark-go/internal/compile/compile.go (revision 4947cdc739c985f6d86941e22894f5cefe7c9e9a)
1*4947cdc7SCole Faust// Package compile defines the Starlark bytecode compiler.
2*4947cdc7SCole Faust// It is an internal package of the Starlark interpreter and is not directly accessible to clients.
3*4947cdc7SCole Faust//
4*4947cdc7SCole Faust// The compiler generates byte code with optional uint32 operands for a
5*4947cdc7SCole Faust// virtual machine with the following components:
6*4947cdc7SCole Faust//   - a program counter, which is an index into the byte code array.
7*4947cdc7SCole Faust//   - an operand stack, whose maximum size is computed for each function by the compiler.
8*4947cdc7SCole Faust//   - an stack of active iterators.
9*4947cdc7SCole Faust//   - an array of local variables.
10*4947cdc7SCole Faust//     The number of local variables and their indices are computed by the resolver.
11*4947cdc7SCole Faust//     Locals (possibly including parameters) that are shared with nested functions
12*4947cdc7SCole Faust//     are 'cells': their locals array slot will contain a value of type 'cell',
13*4947cdc7SCole Faust//     an indirect value in a box that is explicitly read/updated by instructions.
14*4947cdc7SCole Faust//   - an array of free variables, for nested functions.
15*4947cdc7SCole Faust//     Free variables are a subset of the ancestors' cell variables.
16*4947cdc7SCole Faust//     As with locals and cells, these are computed by the resolver.
17*4947cdc7SCole Faust//   - an array of global variables, shared among all functions in the same module.
18*4947cdc7SCole Faust//     All elements are initially nil.
19*4947cdc7SCole Faust//   - two maps of predeclared and universal identifiers.
20*4947cdc7SCole Faust//
21*4947cdc7SCole Faust// Each function has a line number table that maps each program counter
22*4947cdc7SCole Faust// offset to a source position, including the column number.
23*4947cdc7SCole Faust//
24*4947cdc7SCole Faust// Operands, logically uint32s, are encoded using little-endian 7-bit
25*4947cdc7SCole Faust// varints, the top bit indicating that more bytes follow.
26*4947cdc7SCole Faust//
27*4947cdc7SCole Faustpackage compile // import "go.starlark.net/internal/compile"
28*4947cdc7SCole Faust
29*4947cdc7SCole Faustimport (
30*4947cdc7SCole Faust	"bytes"
31*4947cdc7SCole Faust	"fmt"
32*4947cdc7SCole Faust	"log"
33*4947cdc7SCole Faust	"os"
34*4947cdc7SCole Faust	"path/filepath"
35*4947cdc7SCole Faust	"strconv"
36*4947cdc7SCole Faust	"strings"
37*4947cdc7SCole Faust	"sync"
38*4947cdc7SCole Faust
39*4947cdc7SCole Faust	"go.starlark.net/resolve"
40*4947cdc7SCole Faust	"go.starlark.net/syntax"
41*4947cdc7SCole Faust)
42*4947cdc7SCole Faust
43*4947cdc7SCole Faust// Disassemble causes the assembly code for each function
44*4947cdc7SCole Faust// to be printed to stderr as it is generated.
45*4947cdc7SCole Faustvar Disassemble = false
46*4947cdc7SCole Faust
47*4947cdc7SCole Faustconst debug = false // make code generation verbose, for debugging the compiler
48*4947cdc7SCole Faust
49*4947cdc7SCole Faust// Increment this to force recompilation of saved bytecode files.
50*4947cdc7SCole Faustconst Version = 12
51*4947cdc7SCole Faust
52*4947cdc7SCole Fausttype Opcode uint8
53*4947cdc7SCole Faust
54*4947cdc7SCole Faust// "x DUP x x" is a "stack picture" that describes the state of the
55*4947cdc7SCole Faust// stack before and after execution of the instruction.
56*4947cdc7SCole Faust//
57*4947cdc7SCole Faust// OP<index> indicates an immediate operand that is an index into the
58*4947cdc7SCole Faust// specified table: locals, names, freevars, constants.
59*4947cdc7SCole Faustconst (
60*4947cdc7SCole Faust	NOP Opcode = iota // - NOP -
61*4947cdc7SCole Faust
62*4947cdc7SCole Faust	// stack operations
63*4947cdc7SCole Faust	DUP  //   x DUP x x
64*4947cdc7SCole Faust	DUP2 // x y DUP2 x y x y
65*4947cdc7SCole Faust	POP  //   x POP -
66*4947cdc7SCole Faust	EXCH // x y EXCH y x
67*4947cdc7SCole Faust
68*4947cdc7SCole Faust	// binary comparisons
69*4947cdc7SCole Faust	// (order must match Token)
70*4947cdc7SCole Faust	LT
71*4947cdc7SCole Faust	GT
72*4947cdc7SCole Faust	GE
73*4947cdc7SCole Faust	LE
74*4947cdc7SCole Faust	EQL
75*4947cdc7SCole Faust	NEQ
76*4947cdc7SCole Faust
77*4947cdc7SCole Faust	// binary arithmetic
78*4947cdc7SCole Faust	// (order must match Token)
79*4947cdc7SCole Faust	PLUS
80*4947cdc7SCole Faust	MINUS
81*4947cdc7SCole Faust	STAR
82*4947cdc7SCole Faust	SLASH
83*4947cdc7SCole Faust	SLASHSLASH
84*4947cdc7SCole Faust	PERCENT
85*4947cdc7SCole Faust	AMP
86*4947cdc7SCole Faust	PIPE
87*4947cdc7SCole Faust	CIRCUMFLEX
88*4947cdc7SCole Faust	LTLT
89*4947cdc7SCole Faust	GTGT
90*4947cdc7SCole Faust
91*4947cdc7SCole Faust	IN
92*4947cdc7SCole Faust
93*4947cdc7SCole Faust	// unary operators
94*4947cdc7SCole Faust	UPLUS  // x UPLUS x
95*4947cdc7SCole Faust	UMINUS // x UMINUS -x
96*4947cdc7SCole Faust	TILDE  // x TILDE ~x
97*4947cdc7SCole Faust
98*4947cdc7SCole Faust	NONE      // - NONE None
99*4947cdc7SCole Faust	TRUE      // - TRUE True
100*4947cdc7SCole Faust	FALSE     // - FALSE False
101*4947cdc7SCole Faust	MANDATORY // - MANDATORY Mandatory	     [sentinel value for required kwonly args]
102*4947cdc7SCole Faust
103*4947cdc7SCole Faust	ITERPUSH    //       iterable ITERPUSH    -  [pushes the iterator stack]
104*4947cdc7SCole Faust	ITERPOP     //              - ITERPOP     -    [pops the iterator stack]
105*4947cdc7SCole Faust	NOT         //          value NOT         bool
106*4947cdc7SCole Faust	RETURN      //          value RETURN      -
107*4947cdc7SCole Faust	SETINDEX    //        a i new SETINDEX    -
108*4947cdc7SCole Faust	INDEX       //            a i INDEX       elem
109*4947cdc7SCole Faust	SETDICT     // dict key value SETDICT     -
110*4947cdc7SCole Faust	SETDICTUNIQ // dict key value SETDICTUNIQ -
111*4947cdc7SCole Faust	APPEND      //      list elem APPEND      -
112*4947cdc7SCole Faust	SLICE       //   x lo hi step SLICE       slice
113*4947cdc7SCole Faust	INPLACE_ADD //            x y INPLACE_ADD z      where z is x+y or x.extend(y)
114*4947cdc7SCole Faust	MAKEDICT    //              - MAKEDICT    dict
115*4947cdc7SCole Faust
116*4947cdc7SCole Faust	// --- opcodes with an argument must go below this line ---
117*4947cdc7SCole Faust
118*4947cdc7SCole Faust	// control flow
119*4947cdc7SCole Faust	JMP     //            - JMP<addr>     -
120*4947cdc7SCole Faust	CJMP    //         cond CJMP<addr>    -
121*4947cdc7SCole Faust	ITERJMP //            - ITERJMP<addr> elem   (and fall through) [acts on topmost iterator]
122*4947cdc7SCole Faust	//       or:          - ITERJMP<addr> -      (and jump)
123*4947cdc7SCole Faust
124*4947cdc7SCole Faust	CONSTANT     //                 - CONSTANT<constant>  value
125*4947cdc7SCole Faust	MAKETUPLE    //         x1 ... xn MAKETUPLE<n>        tuple
126*4947cdc7SCole Faust	MAKELIST     //         x1 ... xn MAKELIST<n>         list
127*4947cdc7SCole Faust	MAKEFUNC     // defaults+freevars MAKEFUNC<func>      fn
128*4947cdc7SCole Faust	LOAD         //   from1 ... fromN module LOAD<n>      v1 ... vN
129*4947cdc7SCole Faust	SETLOCAL     //             value SETLOCAL<local>     -
130*4947cdc7SCole Faust	SETGLOBAL    //             value SETGLOBAL<global>   -
131*4947cdc7SCole Faust	LOCAL        //                 - LOCAL<local>        value
132*4947cdc7SCole Faust	FREE         //                 - FREE<freevar>       cell
133*4947cdc7SCole Faust	FREECELL     //                 - FREECELL<freevar>   value       (content of FREE cell)
134*4947cdc7SCole Faust	LOCALCELL    //                 - LOCALCELL<local>    value       (content of LOCAL cell)
135*4947cdc7SCole Faust	SETLOCALCELL //             value SETLOCALCELL<local> -           (set content of LOCAL cell)
136*4947cdc7SCole Faust	GLOBAL       //                 - GLOBAL<global>      value
137*4947cdc7SCole Faust	PREDECLARED  //                 - PREDECLARED<name>   value
138*4947cdc7SCole Faust	UNIVERSAL    //                 - UNIVERSAL<name>     value
139*4947cdc7SCole Faust	ATTR         //                 x ATTR<name>          y           y = x.name
140*4947cdc7SCole Faust	SETFIELD     //               x y SETFIELD<name>      -           x.name = y
141*4947cdc7SCole Faust	UNPACK       //          iterable UNPACK<n>           vn ... v1
142*4947cdc7SCole Faust
143*4947cdc7SCole Faust	// n>>8 is #positional args and n&0xff is #named args (pairs).
144*4947cdc7SCole Faust	CALL        // fn positional named                CALL<n>        result
145*4947cdc7SCole Faust	CALL_VAR    // fn positional named *args          CALL_VAR<n>    result
146*4947cdc7SCole Faust	CALL_KW     // fn positional named       **kwargs CALL_KW<n>     result
147*4947cdc7SCole Faust	CALL_VAR_KW // fn positional named *args **kwargs CALL_VAR_KW<n> result
148*4947cdc7SCole Faust
149*4947cdc7SCole Faust	OpcodeArgMin = JMP
150*4947cdc7SCole Faust	OpcodeMax    = CALL_VAR_KW
151*4947cdc7SCole Faust)
152*4947cdc7SCole Faust
153*4947cdc7SCole Faust// TODO(adonovan): add dynamic checks for missing opcodes in the tables below.
154*4947cdc7SCole Faust
155*4947cdc7SCole Faustvar opcodeNames = [...]string{
156*4947cdc7SCole Faust	AMP:          "amp",
157*4947cdc7SCole Faust	APPEND:       "append",
158*4947cdc7SCole Faust	ATTR:         "attr",
159*4947cdc7SCole Faust	CALL:         "call",
160*4947cdc7SCole Faust	CALL_KW:      "call_kw ",
161*4947cdc7SCole Faust	CALL_VAR:     "call_var",
162*4947cdc7SCole Faust	CALL_VAR_KW:  "call_var_kw",
163*4947cdc7SCole Faust	CIRCUMFLEX:   "circumflex",
164*4947cdc7SCole Faust	CJMP:         "cjmp",
165*4947cdc7SCole Faust	CONSTANT:     "constant",
166*4947cdc7SCole Faust	DUP2:         "dup2",
167*4947cdc7SCole Faust	DUP:          "dup",
168*4947cdc7SCole Faust	EQL:          "eql",
169*4947cdc7SCole Faust	EXCH:         "exch",
170*4947cdc7SCole Faust	FALSE:        "false",
171*4947cdc7SCole Faust	FREE:         "free",
172*4947cdc7SCole Faust	FREECELL:     "freecell",
173*4947cdc7SCole Faust	GE:           "ge",
174*4947cdc7SCole Faust	GLOBAL:       "global",
175*4947cdc7SCole Faust	GT:           "gt",
176*4947cdc7SCole Faust	GTGT:         "gtgt",
177*4947cdc7SCole Faust	IN:           "in",
178*4947cdc7SCole Faust	INDEX:        "index",
179*4947cdc7SCole Faust	INPLACE_ADD:  "inplace_add",
180*4947cdc7SCole Faust	ITERJMP:      "iterjmp",
181*4947cdc7SCole Faust	ITERPOP:      "iterpop",
182*4947cdc7SCole Faust	ITERPUSH:     "iterpush",
183*4947cdc7SCole Faust	JMP:          "jmp",
184*4947cdc7SCole Faust	LE:           "le",
185*4947cdc7SCole Faust	LOAD:         "load",
186*4947cdc7SCole Faust	LOCAL:        "local",
187*4947cdc7SCole Faust	LOCALCELL:    "localcell",
188*4947cdc7SCole Faust	LT:           "lt",
189*4947cdc7SCole Faust	LTLT:         "ltlt",
190*4947cdc7SCole Faust	MAKEDICT:     "makedict",
191*4947cdc7SCole Faust	MAKEFUNC:     "makefunc",
192*4947cdc7SCole Faust	MAKELIST:     "makelist",
193*4947cdc7SCole Faust	MAKETUPLE:    "maketuple",
194*4947cdc7SCole Faust	MANDATORY:    "mandatory",
195*4947cdc7SCole Faust	MINUS:        "minus",
196*4947cdc7SCole Faust	NEQ:          "neq",
197*4947cdc7SCole Faust	NONE:         "none",
198*4947cdc7SCole Faust	NOP:          "nop",
199*4947cdc7SCole Faust	NOT:          "not",
200*4947cdc7SCole Faust	PERCENT:      "percent",
201*4947cdc7SCole Faust	PIPE:         "pipe",
202*4947cdc7SCole Faust	PLUS:         "plus",
203*4947cdc7SCole Faust	POP:          "pop",
204*4947cdc7SCole Faust	PREDECLARED:  "predeclared",
205*4947cdc7SCole Faust	RETURN:       "return",
206*4947cdc7SCole Faust	SETDICT:      "setdict",
207*4947cdc7SCole Faust	SETDICTUNIQ:  "setdictuniq",
208*4947cdc7SCole Faust	SETFIELD:     "setfield",
209*4947cdc7SCole Faust	SETGLOBAL:    "setglobal",
210*4947cdc7SCole Faust	SETINDEX:     "setindex",
211*4947cdc7SCole Faust	SETLOCAL:     "setlocal",
212*4947cdc7SCole Faust	SETLOCALCELL: "setlocalcell",
213*4947cdc7SCole Faust	SLASH:        "slash",
214*4947cdc7SCole Faust	SLASHSLASH:   "slashslash",
215*4947cdc7SCole Faust	SLICE:        "slice",
216*4947cdc7SCole Faust	STAR:         "star",
217*4947cdc7SCole Faust	TILDE:        "tilde",
218*4947cdc7SCole Faust	TRUE:         "true",
219*4947cdc7SCole Faust	UMINUS:       "uminus",
220*4947cdc7SCole Faust	UNIVERSAL:    "universal",
221*4947cdc7SCole Faust	UNPACK:       "unpack",
222*4947cdc7SCole Faust	UPLUS:        "uplus",
223*4947cdc7SCole Faust}
224*4947cdc7SCole Faust
225*4947cdc7SCole Faustconst variableStackEffect = 0x7f
226*4947cdc7SCole Faust
227*4947cdc7SCole Faust// stackEffect records the effect on the size of the operand stack of
228*4947cdc7SCole Faust// each kind of instruction. For some instructions this requires computation.
229*4947cdc7SCole Faustvar stackEffect = [...]int8{
230*4947cdc7SCole Faust	AMP:          -1,
231*4947cdc7SCole Faust	APPEND:       -2,
232*4947cdc7SCole Faust	ATTR:         0,
233*4947cdc7SCole Faust	CALL:         variableStackEffect,
234*4947cdc7SCole Faust	CALL_KW:      variableStackEffect,
235*4947cdc7SCole Faust	CALL_VAR:     variableStackEffect,
236*4947cdc7SCole Faust	CALL_VAR_KW:  variableStackEffect,
237*4947cdc7SCole Faust	CIRCUMFLEX:   -1,
238*4947cdc7SCole Faust	CJMP:         -1,
239*4947cdc7SCole Faust	CONSTANT:     +1,
240*4947cdc7SCole Faust	DUP2:         +2,
241*4947cdc7SCole Faust	DUP:          +1,
242*4947cdc7SCole Faust	EQL:          -1,
243*4947cdc7SCole Faust	FALSE:        +1,
244*4947cdc7SCole Faust	FREE:         +1,
245*4947cdc7SCole Faust	FREECELL:     +1,
246*4947cdc7SCole Faust	GE:           -1,
247*4947cdc7SCole Faust	GLOBAL:       +1,
248*4947cdc7SCole Faust	GT:           -1,
249*4947cdc7SCole Faust	GTGT:         -1,
250*4947cdc7SCole Faust	IN:           -1,
251*4947cdc7SCole Faust	INDEX:        -1,
252*4947cdc7SCole Faust	INPLACE_ADD:  -1,
253*4947cdc7SCole Faust	ITERJMP:      variableStackEffect,
254*4947cdc7SCole Faust	ITERPOP:      0,
255*4947cdc7SCole Faust	ITERPUSH:     -1,
256*4947cdc7SCole Faust	JMP:          0,
257*4947cdc7SCole Faust	LE:           -1,
258*4947cdc7SCole Faust	LOAD:         -1,
259*4947cdc7SCole Faust	LOCAL:        +1,
260*4947cdc7SCole Faust	LOCALCELL:    +1,
261*4947cdc7SCole Faust	LT:           -1,
262*4947cdc7SCole Faust	LTLT:         -1,
263*4947cdc7SCole Faust	MAKEDICT:     +1,
264*4947cdc7SCole Faust	MAKEFUNC:     0,
265*4947cdc7SCole Faust	MAKELIST:     variableStackEffect,
266*4947cdc7SCole Faust	MAKETUPLE:    variableStackEffect,
267*4947cdc7SCole Faust	MANDATORY:    +1,
268*4947cdc7SCole Faust	MINUS:        -1,
269*4947cdc7SCole Faust	NEQ:          -1,
270*4947cdc7SCole Faust	NONE:         +1,
271*4947cdc7SCole Faust	NOP:          0,
272*4947cdc7SCole Faust	NOT:          0,
273*4947cdc7SCole Faust	PERCENT:      -1,
274*4947cdc7SCole Faust	PIPE:         -1,
275*4947cdc7SCole Faust	PLUS:         -1,
276*4947cdc7SCole Faust	POP:          -1,
277*4947cdc7SCole Faust	PREDECLARED:  +1,
278*4947cdc7SCole Faust	RETURN:       -1,
279*4947cdc7SCole Faust	SETLOCALCELL: -1,
280*4947cdc7SCole Faust	SETDICT:      -3,
281*4947cdc7SCole Faust	SETDICTUNIQ:  -3,
282*4947cdc7SCole Faust	SETFIELD:     -2,
283*4947cdc7SCole Faust	SETGLOBAL:    -1,
284*4947cdc7SCole Faust	SETINDEX:     -3,
285*4947cdc7SCole Faust	SETLOCAL:     -1,
286*4947cdc7SCole Faust	SLASH:        -1,
287*4947cdc7SCole Faust	SLASHSLASH:   -1,
288*4947cdc7SCole Faust	SLICE:        -3,
289*4947cdc7SCole Faust	STAR:         -1,
290*4947cdc7SCole Faust	TRUE:         +1,
291*4947cdc7SCole Faust	UMINUS:       0,
292*4947cdc7SCole Faust	UNIVERSAL:    +1,
293*4947cdc7SCole Faust	UNPACK:       variableStackEffect,
294*4947cdc7SCole Faust	UPLUS:        0,
295*4947cdc7SCole Faust}
296*4947cdc7SCole Faust
297*4947cdc7SCole Faustfunc (op Opcode) String() string {
298*4947cdc7SCole Faust	if op < OpcodeMax {
299*4947cdc7SCole Faust		if name := opcodeNames[op]; name != "" {
300*4947cdc7SCole Faust			return name
301*4947cdc7SCole Faust		}
302*4947cdc7SCole Faust	}
303*4947cdc7SCole Faust	return fmt.Sprintf("illegal op (%d)", op)
304*4947cdc7SCole Faust}
305*4947cdc7SCole Faust
306*4947cdc7SCole Faust// A Program is a Starlark file in executable form.
307*4947cdc7SCole Faust//
308*4947cdc7SCole Faust// Programs are serialized by the Program.Encode method,
309*4947cdc7SCole Faust// which must be updated whenever this declaration is changed.
310*4947cdc7SCole Fausttype Program struct {
311*4947cdc7SCole Faust	Loads     []Binding     // name (really, string) and position of each load stmt
312*4947cdc7SCole Faust	Names     []string      // names of attributes and predeclared variables
313*4947cdc7SCole Faust	Constants []interface{} // = string | int64 | float64 | *big.Int | Bytes
314*4947cdc7SCole Faust	Functions []*Funcode
315*4947cdc7SCole Faust	Globals   []Binding // for error messages and tracing
316*4947cdc7SCole Faust	Toplevel  *Funcode  // module initialization function
317*4947cdc7SCole Faust}
318*4947cdc7SCole Faust
319*4947cdc7SCole Faust// The type of a bytes literal value, to distinguish from text string.
320*4947cdc7SCole Fausttype Bytes string
321*4947cdc7SCole Faust
322*4947cdc7SCole Faust// A Funcode is the code of a compiled Starlark function.
323*4947cdc7SCole Faust//
324*4947cdc7SCole Faust// Funcodes are serialized by the encoder.function method,
325*4947cdc7SCole Faust// which must be updated whenever this declaration is changed.
326*4947cdc7SCole Fausttype Funcode struct {
327*4947cdc7SCole Faust	Prog                  *Program
328*4947cdc7SCole Faust	Pos                   syntax.Position // position of def or lambda token
329*4947cdc7SCole Faust	Name                  string          // name of this function
330*4947cdc7SCole Faust	Doc                   string          // docstring of this function
331*4947cdc7SCole Faust	Code                  []byte          // the byte code
332*4947cdc7SCole Faust	pclinetab             []uint16        // mapping from pc to linenum
333*4947cdc7SCole Faust	Locals                []Binding       // locals, parameters first
334*4947cdc7SCole Faust	Cells                 []int           // indices of Locals that require cells
335*4947cdc7SCole Faust	Freevars              []Binding       // for tracing
336*4947cdc7SCole Faust	MaxStack              int
337*4947cdc7SCole Faust	NumParams             int
338*4947cdc7SCole Faust	NumKwonlyParams       int
339*4947cdc7SCole Faust	HasVarargs, HasKwargs bool
340*4947cdc7SCole Faust
341*4947cdc7SCole Faust	// -- transient state --
342*4947cdc7SCole Faust
343*4947cdc7SCole Faust	lntOnce sync.Once
344*4947cdc7SCole Faust	lnt     []pclinecol // decoded line number table
345*4947cdc7SCole Faust}
346*4947cdc7SCole Faust
347*4947cdc7SCole Fausttype pclinecol struct {
348*4947cdc7SCole Faust	pc        uint32
349*4947cdc7SCole Faust	line, col int32
350*4947cdc7SCole Faust}
351*4947cdc7SCole Faust
352*4947cdc7SCole Faust// A Binding is the name and position of a binding identifier.
353*4947cdc7SCole Fausttype Binding struct {
354*4947cdc7SCole Faust	Name string
355*4947cdc7SCole Faust	Pos  syntax.Position
356*4947cdc7SCole Faust}
357*4947cdc7SCole Faust
358*4947cdc7SCole Faust// A pcomp holds the compiler state for a Program.
359*4947cdc7SCole Fausttype pcomp struct {
360*4947cdc7SCole Faust	prog *Program // what we're building
361*4947cdc7SCole Faust
362*4947cdc7SCole Faust	names     map[string]uint32
363*4947cdc7SCole Faust	constants map[interface{}]uint32
364*4947cdc7SCole Faust	functions map[*Funcode]uint32
365*4947cdc7SCole Faust}
366*4947cdc7SCole Faust
367*4947cdc7SCole Faust// An fcomp holds the compiler state for a Funcode.
368*4947cdc7SCole Fausttype fcomp struct {
369*4947cdc7SCole Faust	fn *Funcode // what we're building
370*4947cdc7SCole Faust
371*4947cdc7SCole Faust	pcomp *pcomp
372*4947cdc7SCole Faust	pos   syntax.Position // current position of generated code
373*4947cdc7SCole Faust	loops []loop
374*4947cdc7SCole Faust	block *block
375*4947cdc7SCole Faust}
376*4947cdc7SCole Faust
377*4947cdc7SCole Fausttype loop struct {
378*4947cdc7SCole Faust	break_, continue_ *block
379*4947cdc7SCole Faust}
380*4947cdc7SCole Faust
381*4947cdc7SCole Fausttype block struct {
382*4947cdc7SCole Faust	insns []insn
383*4947cdc7SCole Faust
384*4947cdc7SCole Faust	// If the last insn is a RETURN, jmp and cjmp are nil.
385*4947cdc7SCole Faust	// If the last insn is a CJMP or ITERJMP,
386*4947cdc7SCole Faust	//  cjmp and jmp are the "true" and "false" successors.
387*4947cdc7SCole Faust	// Otherwise, jmp is the sole successor.
388*4947cdc7SCole Faust	jmp, cjmp *block
389*4947cdc7SCole Faust
390*4947cdc7SCole Faust	initialstack int // for stack depth computation
391*4947cdc7SCole Faust
392*4947cdc7SCole Faust	// Used during encoding
393*4947cdc7SCole Faust	index int // -1 => not encoded yet
394*4947cdc7SCole Faust	addr  uint32
395*4947cdc7SCole Faust}
396*4947cdc7SCole Faust
397*4947cdc7SCole Fausttype insn struct {
398*4947cdc7SCole Faust	op        Opcode
399*4947cdc7SCole Faust	arg       uint32
400*4947cdc7SCole Faust	line, col int32
401*4947cdc7SCole Faust}
402*4947cdc7SCole Faust
403*4947cdc7SCole Faust// Position returns the source position for program counter pc.
404*4947cdc7SCole Faustfunc (fn *Funcode) Position(pc uint32) syntax.Position {
405*4947cdc7SCole Faust	fn.lntOnce.Do(fn.decodeLNT)
406*4947cdc7SCole Faust
407*4947cdc7SCole Faust	// Binary search to find last LNT entry not greater than pc.
408*4947cdc7SCole Faust	// To avoid dynamic dispatch, this is a specialization of
409*4947cdc7SCole Faust	// sort.Search using this predicate:
410*4947cdc7SCole Faust	//   !(i < len(fn.lnt)-1 && fn.lnt[i+1].pc <= pc)
411*4947cdc7SCole Faust	n := len(fn.lnt)
412*4947cdc7SCole Faust	i, j := 0, n
413*4947cdc7SCole Faust	for i < j {
414*4947cdc7SCole Faust		h := int(uint(i+j) >> 1)
415*4947cdc7SCole Faust		if !(h >= n-1 || fn.lnt[h+1].pc > pc) {
416*4947cdc7SCole Faust			i = h + 1
417*4947cdc7SCole Faust		} else {
418*4947cdc7SCole Faust			j = h
419*4947cdc7SCole Faust		}
420*4947cdc7SCole Faust	}
421*4947cdc7SCole Faust
422*4947cdc7SCole Faust	var line, col int32
423*4947cdc7SCole Faust	if i < n {
424*4947cdc7SCole Faust		line = fn.lnt[i].line
425*4947cdc7SCole Faust		col = fn.lnt[i].col
426*4947cdc7SCole Faust	}
427*4947cdc7SCole Faust
428*4947cdc7SCole Faust	pos := fn.Pos // copy the (annoyingly inaccessible) filename
429*4947cdc7SCole Faust	pos.Col = col
430*4947cdc7SCole Faust	pos.Line = line
431*4947cdc7SCole Faust	return pos
432*4947cdc7SCole Faust}
433*4947cdc7SCole Faust
434*4947cdc7SCole Faust// decodeLNT decodes the line number table and populates fn.lnt.
435*4947cdc7SCole Faust// It is called at most once.
436*4947cdc7SCole Faustfunc (fn *Funcode) decodeLNT() {
437*4947cdc7SCole Faust	// Conceptually the table contains rows of the form
438*4947cdc7SCole Faust	// (pc uint32, line int32, col int32), sorted by pc.
439*4947cdc7SCole Faust	// We use a delta encoding, since the differences
440*4947cdc7SCole Faust	// between successive pc, line, and column values
441*4947cdc7SCole Faust	// are typically small and positive (though line and
442*4947cdc7SCole Faust	// especially column differences may be negative).
443*4947cdc7SCole Faust	// The delta encoding starts from
444*4947cdc7SCole Faust	// {pc: 0, line: fn.Pos.Line, col: fn.Pos.Col}.
445*4947cdc7SCole Faust	//
446*4947cdc7SCole Faust	// Each entry is packed into one or more 16-bit values:
447*4947cdc7SCole Faust	//    Δpc        uint4
448*4947cdc7SCole Faust	//    Δline      int5
449*4947cdc7SCole Faust	//    Δcol       int6
450*4947cdc7SCole Faust	//    incomplete uint1
451*4947cdc7SCole Faust	// The top 4 bits are the unsigned delta pc.
452*4947cdc7SCole Faust	// The next 5 bits are the signed line number delta.
453*4947cdc7SCole Faust	// The next 6 bits are the signed column number delta.
454*4947cdc7SCole Faust	// The bottom bit indicates that more rows follow because
455*4947cdc7SCole Faust	// one of the deltas was maxed out.
456*4947cdc7SCole Faust	// These field widths were chosen from a sample of real programs,
457*4947cdc7SCole Faust	// and allow >97% of rows to be encoded in a single uint16.
458*4947cdc7SCole Faust
459*4947cdc7SCole Faust	fn.lnt = make([]pclinecol, 0, len(fn.pclinetab)) // a minor overapproximation
460*4947cdc7SCole Faust	entry := pclinecol{
461*4947cdc7SCole Faust		pc:   0,
462*4947cdc7SCole Faust		line: fn.Pos.Line,
463*4947cdc7SCole Faust		col:  fn.Pos.Col,
464*4947cdc7SCole Faust	}
465*4947cdc7SCole Faust	for _, x := range fn.pclinetab {
466*4947cdc7SCole Faust		entry.pc += uint32(x) >> 12
467*4947cdc7SCole Faust		entry.line += int32((int16(x) << 4) >> (16 - 5)) // sign extend Δline
468*4947cdc7SCole Faust		entry.col += int32((int16(x) << 9) >> (16 - 6))  // sign extend Δcol
469*4947cdc7SCole Faust		if (x & 1) == 0 {
470*4947cdc7SCole Faust			fn.lnt = append(fn.lnt, entry)
471*4947cdc7SCole Faust		}
472*4947cdc7SCole Faust	}
473*4947cdc7SCole Faust}
474*4947cdc7SCole Faust
475*4947cdc7SCole Faust// bindings converts resolve.Bindings to compiled form.
476*4947cdc7SCole Faustfunc bindings(bindings []*resolve.Binding) []Binding {
477*4947cdc7SCole Faust	res := make([]Binding, len(bindings))
478*4947cdc7SCole Faust	for i, bind := range bindings {
479*4947cdc7SCole Faust		res[i].Name = bind.First.Name
480*4947cdc7SCole Faust		res[i].Pos = bind.First.NamePos
481*4947cdc7SCole Faust	}
482*4947cdc7SCole Faust	return res
483*4947cdc7SCole Faust}
484*4947cdc7SCole Faust
485*4947cdc7SCole Faust// Expr compiles an expression to a program whose toplevel function evaluates it.
486*4947cdc7SCole Faustfunc Expr(expr syntax.Expr, name string, locals []*resolve.Binding) *Program {
487*4947cdc7SCole Faust	pos := syntax.Start(expr)
488*4947cdc7SCole Faust	stmts := []syntax.Stmt{&syntax.ReturnStmt{Result: expr}}
489*4947cdc7SCole Faust	return File(stmts, pos, name, locals, nil)
490*4947cdc7SCole Faust}
491*4947cdc7SCole Faust
492*4947cdc7SCole Faust// File compiles the statements of a file into a program.
493*4947cdc7SCole Faustfunc File(stmts []syntax.Stmt, pos syntax.Position, name string, locals, globals []*resolve.Binding) *Program {
494*4947cdc7SCole Faust	pcomp := &pcomp{
495*4947cdc7SCole Faust		prog: &Program{
496*4947cdc7SCole Faust			Globals: bindings(globals),
497*4947cdc7SCole Faust		},
498*4947cdc7SCole Faust		names:     make(map[string]uint32),
499*4947cdc7SCole Faust		constants: make(map[interface{}]uint32),
500*4947cdc7SCole Faust		functions: make(map[*Funcode]uint32),
501*4947cdc7SCole Faust	}
502*4947cdc7SCole Faust	pcomp.prog.Toplevel = pcomp.function(name, pos, stmts, locals, nil)
503*4947cdc7SCole Faust
504*4947cdc7SCole Faust	return pcomp.prog
505*4947cdc7SCole Faust}
506*4947cdc7SCole Faust
507*4947cdc7SCole Faustfunc (pcomp *pcomp) function(name string, pos syntax.Position, stmts []syntax.Stmt, locals, freevars []*resolve.Binding) *Funcode {
508*4947cdc7SCole Faust	fcomp := &fcomp{
509*4947cdc7SCole Faust		pcomp: pcomp,
510*4947cdc7SCole Faust		pos:   pos,
511*4947cdc7SCole Faust		fn: &Funcode{
512*4947cdc7SCole Faust			Prog:     pcomp.prog,
513*4947cdc7SCole Faust			Pos:      pos,
514*4947cdc7SCole Faust			Name:     name,
515*4947cdc7SCole Faust			Doc:      docStringFromBody(stmts),
516*4947cdc7SCole Faust			Locals:   bindings(locals),
517*4947cdc7SCole Faust			Freevars: bindings(freevars),
518*4947cdc7SCole Faust		},
519*4947cdc7SCole Faust	}
520*4947cdc7SCole Faust
521*4947cdc7SCole Faust	// Record indices of locals that require cells.
522*4947cdc7SCole Faust	for i, local := range locals {
523*4947cdc7SCole Faust		if local.Scope == resolve.Cell {
524*4947cdc7SCole Faust			fcomp.fn.Cells = append(fcomp.fn.Cells, i)
525*4947cdc7SCole Faust		}
526*4947cdc7SCole Faust	}
527*4947cdc7SCole Faust
528*4947cdc7SCole Faust	if debug {
529*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "start function(%s @ %s)\n", name, pos)
530*4947cdc7SCole Faust	}
531*4947cdc7SCole Faust
532*4947cdc7SCole Faust	// Convert AST to a CFG of instructions.
533*4947cdc7SCole Faust	entry := fcomp.newBlock()
534*4947cdc7SCole Faust	fcomp.block = entry
535*4947cdc7SCole Faust	fcomp.stmts(stmts)
536*4947cdc7SCole Faust	if fcomp.block != nil {
537*4947cdc7SCole Faust		fcomp.emit(NONE)
538*4947cdc7SCole Faust		fcomp.emit(RETURN)
539*4947cdc7SCole Faust	}
540*4947cdc7SCole Faust
541*4947cdc7SCole Faust	var oops bool // something bad happened
542*4947cdc7SCole Faust
543*4947cdc7SCole Faust	setinitialstack := func(b *block, depth int) {
544*4947cdc7SCole Faust		if b.initialstack == -1 {
545*4947cdc7SCole Faust			b.initialstack = depth
546*4947cdc7SCole Faust		} else if b.initialstack != depth {
547*4947cdc7SCole Faust			fmt.Fprintf(os.Stderr, "%d: setinitialstack: depth mismatch: %d vs %d\n",
548*4947cdc7SCole Faust				b.index, b.initialstack, depth)
549*4947cdc7SCole Faust			oops = true
550*4947cdc7SCole Faust		}
551*4947cdc7SCole Faust	}
552*4947cdc7SCole Faust
553*4947cdc7SCole Faust	// Linearize the CFG:
554*4947cdc7SCole Faust	// compute order, address, and initial
555*4947cdc7SCole Faust	// stack depth of each reachable block.
556*4947cdc7SCole Faust	var pc uint32
557*4947cdc7SCole Faust	var blocks []*block
558*4947cdc7SCole Faust	var maxstack int
559*4947cdc7SCole Faust	var visit func(b *block)
560*4947cdc7SCole Faust	visit = func(b *block) {
561*4947cdc7SCole Faust		if b.index >= 0 {
562*4947cdc7SCole Faust			return // already visited
563*4947cdc7SCole Faust		}
564*4947cdc7SCole Faust		b.index = len(blocks)
565*4947cdc7SCole Faust		b.addr = pc
566*4947cdc7SCole Faust		blocks = append(blocks, b)
567*4947cdc7SCole Faust
568*4947cdc7SCole Faust		stack := b.initialstack
569*4947cdc7SCole Faust		if debug {
570*4947cdc7SCole Faust			fmt.Fprintf(os.Stderr, "%s block %d: (stack = %d)\n", name, b.index, stack)
571*4947cdc7SCole Faust		}
572*4947cdc7SCole Faust		var cjmpAddr *uint32
573*4947cdc7SCole Faust		var isiterjmp int
574*4947cdc7SCole Faust		for i, insn := range b.insns {
575*4947cdc7SCole Faust			pc++
576*4947cdc7SCole Faust
577*4947cdc7SCole Faust			// Compute size of argument.
578*4947cdc7SCole Faust			if insn.op >= OpcodeArgMin {
579*4947cdc7SCole Faust				switch insn.op {
580*4947cdc7SCole Faust				case ITERJMP:
581*4947cdc7SCole Faust					isiterjmp = 1
582*4947cdc7SCole Faust					fallthrough
583*4947cdc7SCole Faust				case CJMP:
584*4947cdc7SCole Faust					cjmpAddr = &b.insns[i].arg
585*4947cdc7SCole Faust					pc += 4
586*4947cdc7SCole Faust				default:
587*4947cdc7SCole Faust					pc += uint32(argLen(insn.arg))
588*4947cdc7SCole Faust				}
589*4947cdc7SCole Faust			}
590*4947cdc7SCole Faust
591*4947cdc7SCole Faust			// Compute effect on stack.
592*4947cdc7SCole Faust			se := insn.stackeffect()
593*4947cdc7SCole Faust			if debug {
594*4947cdc7SCole Faust				fmt.Fprintln(os.Stderr, "\t", insn.op, stack, stack+se)
595*4947cdc7SCole Faust			}
596*4947cdc7SCole Faust			stack += se
597*4947cdc7SCole Faust			if stack < 0 {
598*4947cdc7SCole Faust				fmt.Fprintf(os.Stderr, "After pc=%d: stack underflow\n", pc)
599*4947cdc7SCole Faust				oops = true
600*4947cdc7SCole Faust			}
601*4947cdc7SCole Faust			if stack+isiterjmp > maxstack {
602*4947cdc7SCole Faust				maxstack = stack + isiterjmp
603*4947cdc7SCole Faust			}
604*4947cdc7SCole Faust		}
605*4947cdc7SCole Faust
606*4947cdc7SCole Faust		if debug {
607*4947cdc7SCole Faust			fmt.Fprintf(os.Stderr, "successors of block %d (start=%d):\n",
608*4947cdc7SCole Faust				b.addr, b.index)
609*4947cdc7SCole Faust			if b.jmp != nil {
610*4947cdc7SCole Faust				fmt.Fprintf(os.Stderr, "jmp to %d\n", b.jmp.index)
611*4947cdc7SCole Faust			}
612*4947cdc7SCole Faust			if b.cjmp != nil {
613*4947cdc7SCole Faust				fmt.Fprintf(os.Stderr, "cjmp to %d\n", b.cjmp.index)
614*4947cdc7SCole Faust			}
615*4947cdc7SCole Faust		}
616*4947cdc7SCole Faust
617*4947cdc7SCole Faust		// Place the jmp block next.
618*4947cdc7SCole Faust		if b.jmp != nil {
619*4947cdc7SCole Faust			// jump threading (empty cycles are impossible)
620*4947cdc7SCole Faust			for b.jmp.insns == nil {
621*4947cdc7SCole Faust				b.jmp = b.jmp.jmp
622*4947cdc7SCole Faust			}
623*4947cdc7SCole Faust
624*4947cdc7SCole Faust			setinitialstack(b.jmp, stack+isiterjmp)
625*4947cdc7SCole Faust			if b.jmp.index < 0 {
626*4947cdc7SCole Faust				// Successor is not yet visited:
627*4947cdc7SCole Faust				// place it next and fall through.
628*4947cdc7SCole Faust				visit(b.jmp)
629*4947cdc7SCole Faust			} else {
630*4947cdc7SCole Faust				// Successor already visited;
631*4947cdc7SCole Faust				// explicit backward jump required.
632*4947cdc7SCole Faust				pc += 5
633*4947cdc7SCole Faust			}
634*4947cdc7SCole Faust		}
635*4947cdc7SCole Faust
636*4947cdc7SCole Faust		// Then the cjmp block.
637*4947cdc7SCole Faust		if b.cjmp != nil {
638*4947cdc7SCole Faust			// jump threading (empty cycles are impossible)
639*4947cdc7SCole Faust			for b.cjmp.insns == nil {
640*4947cdc7SCole Faust				b.cjmp = b.cjmp.jmp
641*4947cdc7SCole Faust			}
642*4947cdc7SCole Faust
643*4947cdc7SCole Faust			setinitialstack(b.cjmp, stack)
644*4947cdc7SCole Faust			visit(b.cjmp)
645*4947cdc7SCole Faust
646*4947cdc7SCole Faust			// Patch the CJMP/ITERJMP, if present.
647*4947cdc7SCole Faust			if cjmpAddr != nil {
648*4947cdc7SCole Faust				*cjmpAddr = b.cjmp.addr
649*4947cdc7SCole Faust			}
650*4947cdc7SCole Faust		}
651*4947cdc7SCole Faust	}
652*4947cdc7SCole Faust	setinitialstack(entry, 0)
653*4947cdc7SCole Faust	visit(entry)
654*4947cdc7SCole Faust
655*4947cdc7SCole Faust	fn := fcomp.fn
656*4947cdc7SCole Faust	fn.MaxStack = maxstack
657*4947cdc7SCole Faust
658*4947cdc7SCole Faust	// Emit bytecode (and position table).
659*4947cdc7SCole Faust	if Disassemble {
660*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "Function %s: (%d blocks, %d bytes)\n", name, len(blocks), pc)
661*4947cdc7SCole Faust	}
662*4947cdc7SCole Faust	fcomp.generate(blocks, pc)
663*4947cdc7SCole Faust
664*4947cdc7SCole Faust	if debug {
665*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "code=%d maxstack=%d\n", fn.Code, fn.MaxStack)
666*4947cdc7SCole Faust	}
667*4947cdc7SCole Faust
668*4947cdc7SCole Faust	// Don't panic until we've completed printing of the function.
669*4947cdc7SCole Faust	if oops {
670*4947cdc7SCole Faust		panic("internal error")
671*4947cdc7SCole Faust	}
672*4947cdc7SCole Faust
673*4947cdc7SCole Faust	if debug {
674*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "end function(%s @ %s)\n", name, pos)
675*4947cdc7SCole Faust	}
676*4947cdc7SCole Faust
677*4947cdc7SCole Faust	return fn
678*4947cdc7SCole Faust}
679*4947cdc7SCole Faust
680*4947cdc7SCole Faustfunc docStringFromBody(body []syntax.Stmt) string {
681*4947cdc7SCole Faust	if len(body) == 0 {
682*4947cdc7SCole Faust		return ""
683*4947cdc7SCole Faust	}
684*4947cdc7SCole Faust	expr, ok := body[0].(*syntax.ExprStmt)
685*4947cdc7SCole Faust	if !ok {
686*4947cdc7SCole Faust		return ""
687*4947cdc7SCole Faust	}
688*4947cdc7SCole Faust	lit, ok := expr.X.(*syntax.Literal)
689*4947cdc7SCole Faust	if !ok {
690*4947cdc7SCole Faust		return ""
691*4947cdc7SCole Faust	}
692*4947cdc7SCole Faust	if lit.Token != syntax.STRING {
693*4947cdc7SCole Faust		return ""
694*4947cdc7SCole Faust	}
695*4947cdc7SCole Faust	return lit.Value.(string)
696*4947cdc7SCole Faust}
697*4947cdc7SCole Faust
698*4947cdc7SCole Faustfunc (insn *insn) stackeffect() int {
699*4947cdc7SCole Faust	se := int(stackEffect[insn.op])
700*4947cdc7SCole Faust	if se == variableStackEffect {
701*4947cdc7SCole Faust		arg := int(insn.arg)
702*4947cdc7SCole Faust		switch insn.op {
703*4947cdc7SCole Faust		case CALL, CALL_KW, CALL_VAR, CALL_VAR_KW:
704*4947cdc7SCole Faust			se = -int(2*(insn.arg&0xff) + insn.arg>>8)
705*4947cdc7SCole Faust			if insn.op != CALL {
706*4947cdc7SCole Faust				se--
707*4947cdc7SCole Faust			}
708*4947cdc7SCole Faust			if insn.op == CALL_VAR_KW {
709*4947cdc7SCole Faust				se--
710*4947cdc7SCole Faust			}
711*4947cdc7SCole Faust		case ITERJMP:
712*4947cdc7SCole Faust			// Stack effect differs by successor:
713*4947cdc7SCole Faust			// +1 for jmp/false/ok
714*4947cdc7SCole Faust			//  0 for cjmp/true/exhausted
715*4947cdc7SCole Faust			// Handled specially in caller.
716*4947cdc7SCole Faust			se = 0
717*4947cdc7SCole Faust		case MAKELIST, MAKETUPLE:
718*4947cdc7SCole Faust			se = 1 - arg
719*4947cdc7SCole Faust		case UNPACK:
720*4947cdc7SCole Faust			se = arg - 1
721*4947cdc7SCole Faust		default:
722*4947cdc7SCole Faust			panic(insn.op)
723*4947cdc7SCole Faust		}
724*4947cdc7SCole Faust	}
725*4947cdc7SCole Faust	return se
726*4947cdc7SCole Faust}
727*4947cdc7SCole Faust
728*4947cdc7SCole Faust// generate emits the linear instruction stream from the CFG,
729*4947cdc7SCole Faust// and builds the PC-to-line number table.
730*4947cdc7SCole Faustfunc (fcomp *fcomp) generate(blocks []*block, codelen uint32) {
731*4947cdc7SCole Faust	code := make([]byte, 0, codelen)
732*4947cdc7SCole Faust	var pclinetab []uint16
733*4947cdc7SCole Faust	prev := pclinecol{
734*4947cdc7SCole Faust		pc:   0,
735*4947cdc7SCole Faust		line: fcomp.fn.Pos.Line,
736*4947cdc7SCole Faust		col:  fcomp.fn.Pos.Col,
737*4947cdc7SCole Faust	}
738*4947cdc7SCole Faust
739*4947cdc7SCole Faust	for _, b := range blocks {
740*4947cdc7SCole Faust		if Disassemble {
741*4947cdc7SCole Faust			fmt.Fprintf(os.Stderr, "%d:\n", b.index)
742*4947cdc7SCole Faust		}
743*4947cdc7SCole Faust		pc := b.addr
744*4947cdc7SCole Faust		for _, insn := range b.insns {
745*4947cdc7SCole Faust			if insn.line != 0 {
746*4947cdc7SCole Faust				// Instruction has a source position.  Delta-encode it.
747*4947cdc7SCole Faust				// See Funcode.Position for the encoding.
748*4947cdc7SCole Faust				for {
749*4947cdc7SCole Faust					var incomplete uint16
750*4947cdc7SCole Faust
751*4947cdc7SCole Faust					// Δpc, uint4
752*4947cdc7SCole Faust					deltapc := pc - prev.pc
753*4947cdc7SCole Faust					if deltapc > 0x0f {
754*4947cdc7SCole Faust						deltapc = 0x0f
755*4947cdc7SCole Faust						incomplete = 1
756*4947cdc7SCole Faust					}
757*4947cdc7SCole Faust					prev.pc += deltapc
758*4947cdc7SCole Faust
759*4947cdc7SCole Faust					// Δline, int5
760*4947cdc7SCole Faust					deltaline, ok := clip(insn.line-prev.line, -0x10, 0x0f)
761*4947cdc7SCole Faust					if !ok {
762*4947cdc7SCole Faust						incomplete = 1
763*4947cdc7SCole Faust					}
764*4947cdc7SCole Faust					prev.line += deltaline
765*4947cdc7SCole Faust
766*4947cdc7SCole Faust					// Δcol, int6
767*4947cdc7SCole Faust					deltacol, ok := clip(insn.col-prev.col, -0x20, 0x1f)
768*4947cdc7SCole Faust					if !ok {
769*4947cdc7SCole Faust						incomplete = 1
770*4947cdc7SCole Faust					}
771*4947cdc7SCole Faust					prev.col += deltacol
772*4947cdc7SCole Faust
773*4947cdc7SCole Faust					entry := uint16(deltapc<<12) | uint16(deltaline&0x1f)<<7 | uint16(deltacol&0x3f)<<1 | incomplete
774*4947cdc7SCole Faust					pclinetab = append(pclinetab, entry)
775*4947cdc7SCole Faust					if incomplete == 0 {
776*4947cdc7SCole Faust						break
777*4947cdc7SCole Faust					}
778*4947cdc7SCole Faust				}
779*4947cdc7SCole Faust
780*4947cdc7SCole Faust				if Disassemble {
781*4947cdc7SCole Faust					fmt.Fprintf(os.Stderr, "\t\t\t\t\t; %s:%d:%d\n",
782*4947cdc7SCole Faust						filepath.Base(fcomp.fn.Pos.Filename()), insn.line, insn.col)
783*4947cdc7SCole Faust				}
784*4947cdc7SCole Faust			}
785*4947cdc7SCole Faust			if Disassemble {
786*4947cdc7SCole Faust				PrintOp(fcomp.fn, pc, insn.op, insn.arg)
787*4947cdc7SCole Faust			}
788*4947cdc7SCole Faust			code = append(code, byte(insn.op))
789*4947cdc7SCole Faust			pc++
790*4947cdc7SCole Faust			if insn.op >= OpcodeArgMin {
791*4947cdc7SCole Faust				if insn.op == CJMP || insn.op == ITERJMP {
792*4947cdc7SCole Faust					code = addUint32(code, insn.arg, 4) // pad arg to 4 bytes
793*4947cdc7SCole Faust				} else {
794*4947cdc7SCole Faust					code = addUint32(code, insn.arg, 0)
795*4947cdc7SCole Faust				}
796*4947cdc7SCole Faust				pc = uint32(len(code))
797*4947cdc7SCole Faust			}
798*4947cdc7SCole Faust		}
799*4947cdc7SCole Faust
800*4947cdc7SCole Faust		if b.jmp != nil && b.jmp.index != b.index+1 {
801*4947cdc7SCole Faust			addr := b.jmp.addr
802*4947cdc7SCole Faust			if Disassemble {
803*4947cdc7SCole Faust				fmt.Fprintf(os.Stderr, "\t%d\tjmp\t\t%d\t; block %d\n",
804*4947cdc7SCole Faust					pc, addr, b.jmp.index)
805*4947cdc7SCole Faust			}
806*4947cdc7SCole Faust			code = append(code, byte(JMP))
807*4947cdc7SCole Faust			code = addUint32(code, addr, 4)
808*4947cdc7SCole Faust		}
809*4947cdc7SCole Faust	}
810*4947cdc7SCole Faust	if len(code) != int(codelen) {
811*4947cdc7SCole Faust		panic("internal error: wrong code length")
812*4947cdc7SCole Faust	}
813*4947cdc7SCole Faust
814*4947cdc7SCole Faust	fcomp.fn.pclinetab = pclinetab
815*4947cdc7SCole Faust	fcomp.fn.Code = code
816*4947cdc7SCole Faust}
817*4947cdc7SCole Faust
818*4947cdc7SCole Faust// clip returns the value nearest x in the range [min...max],
819*4947cdc7SCole Faust// and whether it equals x.
820*4947cdc7SCole Faustfunc clip(x, min, max int32) (int32, bool) {
821*4947cdc7SCole Faust	if x > max {
822*4947cdc7SCole Faust		return max, false
823*4947cdc7SCole Faust	} else if x < min {
824*4947cdc7SCole Faust		return min, false
825*4947cdc7SCole Faust	} else {
826*4947cdc7SCole Faust		return x, true
827*4947cdc7SCole Faust	}
828*4947cdc7SCole Faust}
829*4947cdc7SCole Faust
830*4947cdc7SCole Faust// addUint32 encodes x as 7-bit little-endian varint.
831*4947cdc7SCole Faust// TODO(adonovan): opt: steal top two bits of opcode
832*4947cdc7SCole Faust// to encode the number of complete bytes that follow.
833*4947cdc7SCole Faustfunc addUint32(code []byte, x uint32, min int) []byte {
834*4947cdc7SCole Faust	end := len(code) + min
835*4947cdc7SCole Faust	for x >= 0x80 {
836*4947cdc7SCole Faust		code = append(code, byte(x)|0x80)
837*4947cdc7SCole Faust		x >>= 7
838*4947cdc7SCole Faust	}
839*4947cdc7SCole Faust	code = append(code, byte(x))
840*4947cdc7SCole Faust	// Pad the operand with NOPs to exactly min bytes.
841*4947cdc7SCole Faust	for len(code) < end {
842*4947cdc7SCole Faust		code = append(code, byte(NOP))
843*4947cdc7SCole Faust	}
844*4947cdc7SCole Faust	return code
845*4947cdc7SCole Faust}
846*4947cdc7SCole Faust
847*4947cdc7SCole Faustfunc argLen(x uint32) int {
848*4947cdc7SCole Faust	n := 0
849*4947cdc7SCole Faust	for x >= 0x80 {
850*4947cdc7SCole Faust		n++
851*4947cdc7SCole Faust		x >>= 7
852*4947cdc7SCole Faust	}
853*4947cdc7SCole Faust	return n + 1
854*4947cdc7SCole Faust}
855*4947cdc7SCole Faust
856*4947cdc7SCole Faust// PrintOp prints an instruction.
857*4947cdc7SCole Faust// It is provided for debugging.
858*4947cdc7SCole Faustfunc PrintOp(fn *Funcode, pc uint32, op Opcode, arg uint32) {
859*4947cdc7SCole Faust	if op < OpcodeArgMin {
860*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "\t%d\t%s\n", pc, op)
861*4947cdc7SCole Faust		return
862*4947cdc7SCole Faust	}
863*4947cdc7SCole Faust
864*4947cdc7SCole Faust	var comment string
865*4947cdc7SCole Faust	switch op {
866*4947cdc7SCole Faust	case CONSTANT:
867*4947cdc7SCole Faust		switch x := fn.Prog.Constants[arg].(type) {
868*4947cdc7SCole Faust		case string:
869*4947cdc7SCole Faust			comment = strconv.Quote(x)
870*4947cdc7SCole Faust		case Bytes:
871*4947cdc7SCole Faust			comment = "b" + strconv.Quote(string(x))
872*4947cdc7SCole Faust		default:
873*4947cdc7SCole Faust			comment = fmt.Sprint(x)
874*4947cdc7SCole Faust		}
875*4947cdc7SCole Faust	case MAKEFUNC:
876*4947cdc7SCole Faust		comment = fn.Prog.Functions[arg].Name
877*4947cdc7SCole Faust	case SETLOCAL, LOCAL:
878*4947cdc7SCole Faust		comment = fn.Locals[arg].Name
879*4947cdc7SCole Faust	case SETGLOBAL, GLOBAL:
880*4947cdc7SCole Faust		comment = fn.Prog.Globals[arg].Name
881*4947cdc7SCole Faust	case ATTR, SETFIELD, PREDECLARED, UNIVERSAL:
882*4947cdc7SCole Faust		comment = fn.Prog.Names[arg]
883*4947cdc7SCole Faust	case FREE:
884*4947cdc7SCole Faust		comment = fn.Freevars[arg].Name
885*4947cdc7SCole Faust	case CALL, CALL_VAR, CALL_KW, CALL_VAR_KW:
886*4947cdc7SCole Faust		comment = fmt.Sprintf("%d pos, %d named", arg>>8, arg&0xff)
887*4947cdc7SCole Faust	default:
888*4947cdc7SCole Faust		// JMP, CJMP, ITERJMP, MAKETUPLE, MAKELIST, LOAD, UNPACK:
889*4947cdc7SCole Faust		// arg is just a number
890*4947cdc7SCole Faust	}
891*4947cdc7SCole Faust	var buf bytes.Buffer
892*4947cdc7SCole Faust	fmt.Fprintf(&buf, "\t%d\t%-10s\t%d", pc, op, arg)
893*4947cdc7SCole Faust	if comment != "" {
894*4947cdc7SCole Faust		fmt.Fprint(&buf, "\t; ", comment)
895*4947cdc7SCole Faust	}
896*4947cdc7SCole Faust	fmt.Fprintln(&buf)
897*4947cdc7SCole Faust	os.Stderr.Write(buf.Bytes())
898*4947cdc7SCole Faust}
899*4947cdc7SCole Faust
900*4947cdc7SCole Faust// newBlock returns a new block.
901*4947cdc7SCole Faustfunc (fcomp) newBlock() *block {
902*4947cdc7SCole Faust	return &block{index: -1, initialstack: -1}
903*4947cdc7SCole Faust}
904*4947cdc7SCole Faust
905*4947cdc7SCole Faust// emit emits an instruction to the current block.
906*4947cdc7SCole Faustfunc (fcomp *fcomp) emit(op Opcode) {
907*4947cdc7SCole Faust	if op >= OpcodeArgMin {
908*4947cdc7SCole Faust		panic("missing arg: " + op.String())
909*4947cdc7SCole Faust	}
910*4947cdc7SCole Faust	insn := insn{op: op, line: fcomp.pos.Line, col: fcomp.pos.Col}
911*4947cdc7SCole Faust	fcomp.block.insns = append(fcomp.block.insns, insn)
912*4947cdc7SCole Faust	fcomp.pos.Line = 0
913*4947cdc7SCole Faust	fcomp.pos.Col = 0
914*4947cdc7SCole Faust}
915*4947cdc7SCole Faust
916*4947cdc7SCole Faust// emit1 emits an instruction with an immediate operand.
917*4947cdc7SCole Faustfunc (fcomp *fcomp) emit1(op Opcode, arg uint32) {
918*4947cdc7SCole Faust	if op < OpcodeArgMin {
919*4947cdc7SCole Faust		panic("unwanted arg: " + op.String())
920*4947cdc7SCole Faust	}
921*4947cdc7SCole Faust	insn := insn{op: op, arg: arg, line: fcomp.pos.Line, col: fcomp.pos.Col}
922*4947cdc7SCole Faust	fcomp.block.insns = append(fcomp.block.insns, insn)
923*4947cdc7SCole Faust	fcomp.pos.Line = 0
924*4947cdc7SCole Faust	fcomp.pos.Col = 0
925*4947cdc7SCole Faust}
926*4947cdc7SCole Faust
927*4947cdc7SCole Faust// jump emits a jump to the specified block.
928*4947cdc7SCole Faust// On return, the current block is unset.
929*4947cdc7SCole Faustfunc (fcomp *fcomp) jump(b *block) {
930*4947cdc7SCole Faust	if b == fcomp.block {
931*4947cdc7SCole Faust		panic("self-jump") // unreachable: Starlark has no arbitrary looping constructs
932*4947cdc7SCole Faust	}
933*4947cdc7SCole Faust	fcomp.block.jmp = b
934*4947cdc7SCole Faust	fcomp.block = nil
935*4947cdc7SCole Faust}
936*4947cdc7SCole Faust
937*4947cdc7SCole Faust// condjump emits a conditional jump (CJMP or ITERJMP)
938*4947cdc7SCole Faust// to the specified true/false blocks.
939*4947cdc7SCole Faust// (For ITERJMP, the cases are jmp/f/ok and cjmp/t/exhausted.)
940*4947cdc7SCole Faust// On return, the current block is unset.
941*4947cdc7SCole Faustfunc (fcomp *fcomp) condjump(op Opcode, t, f *block) {
942*4947cdc7SCole Faust	if !(op == CJMP || op == ITERJMP) {
943*4947cdc7SCole Faust		panic("not a conditional jump: " + op.String())
944*4947cdc7SCole Faust	}
945*4947cdc7SCole Faust	fcomp.emit1(op, 0) // fill in address later
946*4947cdc7SCole Faust	fcomp.block.cjmp = t
947*4947cdc7SCole Faust	fcomp.jump(f)
948*4947cdc7SCole Faust}
949*4947cdc7SCole Faust
950*4947cdc7SCole Faust// nameIndex returns the index of the specified name
951*4947cdc7SCole Faust// within the name pool, adding it if necessary.
952*4947cdc7SCole Faustfunc (pcomp *pcomp) nameIndex(name string) uint32 {
953*4947cdc7SCole Faust	index, ok := pcomp.names[name]
954*4947cdc7SCole Faust	if !ok {
955*4947cdc7SCole Faust		index = uint32(len(pcomp.prog.Names))
956*4947cdc7SCole Faust		pcomp.names[name] = index
957*4947cdc7SCole Faust		pcomp.prog.Names = append(pcomp.prog.Names, name)
958*4947cdc7SCole Faust	}
959*4947cdc7SCole Faust	return index
960*4947cdc7SCole Faust}
961*4947cdc7SCole Faust
962*4947cdc7SCole Faust// constantIndex returns the index of the specified constant
963*4947cdc7SCole Faust// within the constant pool, adding it if necessary.
964*4947cdc7SCole Faustfunc (pcomp *pcomp) constantIndex(v interface{}) uint32 {
965*4947cdc7SCole Faust	index, ok := pcomp.constants[v]
966*4947cdc7SCole Faust	if !ok {
967*4947cdc7SCole Faust		index = uint32(len(pcomp.prog.Constants))
968*4947cdc7SCole Faust		pcomp.constants[v] = index
969*4947cdc7SCole Faust		pcomp.prog.Constants = append(pcomp.prog.Constants, v)
970*4947cdc7SCole Faust	}
971*4947cdc7SCole Faust	return index
972*4947cdc7SCole Faust}
973*4947cdc7SCole Faust
974*4947cdc7SCole Faust// functionIndex returns the index of the specified function
975*4947cdc7SCole Faust// AST the nestedfun pool, adding it if necessary.
976*4947cdc7SCole Faustfunc (pcomp *pcomp) functionIndex(fn *Funcode) uint32 {
977*4947cdc7SCole Faust	index, ok := pcomp.functions[fn]
978*4947cdc7SCole Faust	if !ok {
979*4947cdc7SCole Faust		index = uint32(len(pcomp.prog.Functions))
980*4947cdc7SCole Faust		pcomp.functions[fn] = index
981*4947cdc7SCole Faust		pcomp.prog.Functions = append(pcomp.prog.Functions, fn)
982*4947cdc7SCole Faust	}
983*4947cdc7SCole Faust	return index
984*4947cdc7SCole Faust}
985*4947cdc7SCole Faust
986*4947cdc7SCole Faust// string emits code to push the specified string.
987*4947cdc7SCole Faustfunc (fcomp *fcomp) string(s string) {
988*4947cdc7SCole Faust	fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(s))
989*4947cdc7SCole Faust}
990*4947cdc7SCole Faust
991*4947cdc7SCole Faust// setPos sets the current source position.
992*4947cdc7SCole Faust// It should be called prior to any operation that can fail dynamically.
993*4947cdc7SCole Faust// All positions are assumed to belong to the same file.
994*4947cdc7SCole Faustfunc (fcomp *fcomp) setPos(pos syntax.Position) {
995*4947cdc7SCole Faust	fcomp.pos = pos
996*4947cdc7SCole Faust}
997*4947cdc7SCole Faust
998*4947cdc7SCole Faust// set emits code to store the top-of-stack value
999*4947cdc7SCole Faust// to the specified local, cell, or global variable.
1000*4947cdc7SCole Faustfunc (fcomp *fcomp) set(id *syntax.Ident) {
1001*4947cdc7SCole Faust	bind := id.Binding.(*resolve.Binding)
1002*4947cdc7SCole Faust	switch bind.Scope {
1003*4947cdc7SCole Faust	case resolve.Local:
1004*4947cdc7SCole Faust		fcomp.emit1(SETLOCAL, uint32(bind.Index))
1005*4947cdc7SCole Faust	case resolve.Cell:
1006*4947cdc7SCole Faust		fcomp.emit1(SETLOCALCELL, uint32(bind.Index))
1007*4947cdc7SCole Faust	case resolve.Global:
1008*4947cdc7SCole Faust		fcomp.emit1(SETGLOBAL, uint32(bind.Index))
1009*4947cdc7SCole Faust	default:
1010*4947cdc7SCole Faust		log.Panicf("%s: set(%s): not global/local/cell (%d)", id.NamePos, id.Name, bind.Scope)
1011*4947cdc7SCole Faust	}
1012*4947cdc7SCole Faust}
1013*4947cdc7SCole Faust
1014*4947cdc7SCole Faust// lookup emits code to push the value of the specified variable.
1015*4947cdc7SCole Faustfunc (fcomp *fcomp) lookup(id *syntax.Ident) {
1016*4947cdc7SCole Faust	bind := id.Binding.(*resolve.Binding)
1017*4947cdc7SCole Faust	if bind.Scope != resolve.Universal { // (universal lookup can't fail)
1018*4947cdc7SCole Faust		fcomp.setPos(id.NamePos)
1019*4947cdc7SCole Faust	}
1020*4947cdc7SCole Faust	switch bind.Scope {
1021*4947cdc7SCole Faust	case resolve.Local:
1022*4947cdc7SCole Faust		fcomp.emit1(LOCAL, uint32(bind.Index))
1023*4947cdc7SCole Faust	case resolve.Free:
1024*4947cdc7SCole Faust		fcomp.emit1(FREECELL, uint32(bind.Index))
1025*4947cdc7SCole Faust	case resolve.Cell:
1026*4947cdc7SCole Faust		fcomp.emit1(LOCALCELL, uint32(bind.Index))
1027*4947cdc7SCole Faust	case resolve.Global:
1028*4947cdc7SCole Faust		fcomp.emit1(GLOBAL, uint32(bind.Index))
1029*4947cdc7SCole Faust	case resolve.Predeclared:
1030*4947cdc7SCole Faust		fcomp.emit1(PREDECLARED, fcomp.pcomp.nameIndex(id.Name))
1031*4947cdc7SCole Faust	case resolve.Universal:
1032*4947cdc7SCole Faust		fcomp.emit1(UNIVERSAL, fcomp.pcomp.nameIndex(id.Name))
1033*4947cdc7SCole Faust	default:
1034*4947cdc7SCole Faust		log.Panicf("%s: compiler.lookup(%s): scope = %d", id.NamePos, id.Name, bind.Scope)
1035*4947cdc7SCole Faust	}
1036*4947cdc7SCole Faust}
1037*4947cdc7SCole Faust
1038*4947cdc7SCole Faustfunc (fcomp *fcomp) stmts(stmts []syntax.Stmt) {
1039*4947cdc7SCole Faust	for _, stmt := range stmts {
1040*4947cdc7SCole Faust		fcomp.stmt(stmt)
1041*4947cdc7SCole Faust	}
1042*4947cdc7SCole Faust}
1043*4947cdc7SCole Faust
1044*4947cdc7SCole Faustfunc (fcomp *fcomp) stmt(stmt syntax.Stmt) {
1045*4947cdc7SCole Faust	switch stmt := stmt.(type) {
1046*4947cdc7SCole Faust	case *syntax.ExprStmt:
1047*4947cdc7SCole Faust		if _, ok := stmt.X.(*syntax.Literal); ok {
1048*4947cdc7SCole Faust			// Opt: don't compile doc comments only to pop them.
1049*4947cdc7SCole Faust			return
1050*4947cdc7SCole Faust		}
1051*4947cdc7SCole Faust		fcomp.expr(stmt.X)
1052*4947cdc7SCole Faust		fcomp.emit(POP)
1053*4947cdc7SCole Faust
1054*4947cdc7SCole Faust	case *syntax.BranchStmt:
1055*4947cdc7SCole Faust		// Resolver invariant: break/continue appear only within loops.
1056*4947cdc7SCole Faust		switch stmt.Token {
1057*4947cdc7SCole Faust		case syntax.PASS:
1058*4947cdc7SCole Faust			// no-op
1059*4947cdc7SCole Faust		case syntax.BREAK:
1060*4947cdc7SCole Faust			b := fcomp.loops[len(fcomp.loops)-1].break_
1061*4947cdc7SCole Faust			fcomp.jump(b)
1062*4947cdc7SCole Faust			fcomp.block = fcomp.newBlock() // dead code
1063*4947cdc7SCole Faust		case syntax.CONTINUE:
1064*4947cdc7SCole Faust			b := fcomp.loops[len(fcomp.loops)-1].continue_
1065*4947cdc7SCole Faust			fcomp.jump(b)
1066*4947cdc7SCole Faust			fcomp.block = fcomp.newBlock() // dead code
1067*4947cdc7SCole Faust		}
1068*4947cdc7SCole Faust
1069*4947cdc7SCole Faust	case *syntax.IfStmt:
1070*4947cdc7SCole Faust		// Keep consistent with CondExpr.
1071*4947cdc7SCole Faust		t := fcomp.newBlock()
1072*4947cdc7SCole Faust		f := fcomp.newBlock()
1073*4947cdc7SCole Faust		done := fcomp.newBlock()
1074*4947cdc7SCole Faust
1075*4947cdc7SCole Faust		fcomp.ifelse(stmt.Cond, t, f)
1076*4947cdc7SCole Faust
1077*4947cdc7SCole Faust		fcomp.block = t
1078*4947cdc7SCole Faust		fcomp.stmts(stmt.True)
1079*4947cdc7SCole Faust		fcomp.jump(done)
1080*4947cdc7SCole Faust
1081*4947cdc7SCole Faust		fcomp.block = f
1082*4947cdc7SCole Faust		fcomp.stmts(stmt.False)
1083*4947cdc7SCole Faust		fcomp.jump(done)
1084*4947cdc7SCole Faust
1085*4947cdc7SCole Faust		fcomp.block = done
1086*4947cdc7SCole Faust
1087*4947cdc7SCole Faust	case *syntax.AssignStmt:
1088*4947cdc7SCole Faust		switch stmt.Op {
1089*4947cdc7SCole Faust		case syntax.EQ:
1090*4947cdc7SCole Faust			// simple assignment: x = y
1091*4947cdc7SCole Faust			fcomp.expr(stmt.RHS)
1092*4947cdc7SCole Faust			fcomp.assign(stmt.OpPos, stmt.LHS)
1093*4947cdc7SCole Faust
1094*4947cdc7SCole Faust		case syntax.PLUS_EQ,
1095*4947cdc7SCole Faust			syntax.MINUS_EQ,
1096*4947cdc7SCole Faust			syntax.STAR_EQ,
1097*4947cdc7SCole Faust			syntax.SLASH_EQ,
1098*4947cdc7SCole Faust			syntax.SLASHSLASH_EQ,
1099*4947cdc7SCole Faust			syntax.PERCENT_EQ,
1100*4947cdc7SCole Faust			syntax.AMP_EQ,
1101*4947cdc7SCole Faust			syntax.PIPE_EQ,
1102*4947cdc7SCole Faust			syntax.CIRCUMFLEX_EQ,
1103*4947cdc7SCole Faust			syntax.LTLT_EQ,
1104*4947cdc7SCole Faust			syntax.GTGT_EQ:
1105*4947cdc7SCole Faust			// augmented assignment: x += y
1106*4947cdc7SCole Faust
1107*4947cdc7SCole Faust			var set func()
1108*4947cdc7SCole Faust
1109*4947cdc7SCole Faust			// Evaluate "address" of x exactly once to avoid duplicate side-effects.
1110*4947cdc7SCole Faust			switch lhs := unparen(stmt.LHS).(type) {
1111*4947cdc7SCole Faust			case *syntax.Ident:
1112*4947cdc7SCole Faust				// x = ...
1113*4947cdc7SCole Faust				fcomp.lookup(lhs)
1114*4947cdc7SCole Faust				set = func() {
1115*4947cdc7SCole Faust					fcomp.set(lhs)
1116*4947cdc7SCole Faust				}
1117*4947cdc7SCole Faust
1118*4947cdc7SCole Faust			case *syntax.IndexExpr:
1119*4947cdc7SCole Faust				// x[y] = ...
1120*4947cdc7SCole Faust				fcomp.expr(lhs.X)
1121*4947cdc7SCole Faust				fcomp.expr(lhs.Y)
1122*4947cdc7SCole Faust				fcomp.emit(DUP2)
1123*4947cdc7SCole Faust				fcomp.setPos(lhs.Lbrack)
1124*4947cdc7SCole Faust				fcomp.emit(INDEX)
1125*4947cdc7SCole Faust				set = func() {
1126*4947cdc7SCole Faust					fcomp.setPos(lhs.Lbrack)
1127*4947cdc7SCole Faust					fcomp.emit(SETINDEX)
1128*4947cdc7SCole Faust				}
1129*4947cdc7SCole Faust
1130*4947cdc7SCole Faust			case *syntax.DotExpr:
1131*4947cdc7SCole Faust				// x.f = ...
1132*4947cdc7SCole Faust				fcomp.expr(lhs.X)
1133*4947cdc7SCole Faust				fcomp.emit(DUP)
1134*4947cdc7SCole Faust				name := fcomp.pcomp.nameIndex(lhs.Name.Name)
1135*4947cdc7SCole Faust				fcomp.setPos(lhs.Dot)
1136*4947cdc7SCole Faust				fcomp.emit1(ATTR, name)
1137*4947cdc7SCole Faust				set = func() {
1138*4947cdc7SCole Faust					fcomp.setPos(lhs.Dot)
1139*4947cdc7SCole Faust					fcomp.emit1(SETFIELD, name)
1140*4947cdc7SCole Faust				}
1141*4947cdc7SCole Faust
1142*4947cdc7SCole Faust			default:
1143*4947cdc7SCole Faust				panic(lhs)
1144*4947cdc7SCole Faust			}
1145*4947cdc7SCole Faust
1146*4947cdc7SCole Faust			fcomp.expr(stmt.RHS)
1147*4947cdc7SCole Faust
1148*4947cdc7SCole Faust			if stmt.Op == syntax.PLUS_EQ {
1149*4947cdc7SCole Faust				// Allow the runtime to optimize list += iterable.
1150*4947cdc7SCole Faust				fcomp.setPos(stmt.OpPos)
1151*4947cdc7SCole Faust				fcomp.emit(INPLACE_ADD)
1152*4947cdc7SCole Faust			} else {
1153*4947cdc7SCole Faust				fcomp.binop(stmt.OpPos, stmt.Op-syntax.PLUS_EQ+syntax.PLUS)
1154*4947cdc7SCole Faust			}
1155*4947cdc7SCole Faust			set()
1156*4947cdc7SCole Faust		}
1157*4947cdc7SCole Faust
1158*4947cdc7SCole Faust	case *syntax.DefStmt:
1159*4947cdc7SCole Faust		fcomp.function(stmt.Function.(*resolve.Function))
1160*4947cdc7SCole Faust		fcomp.set(stmt.Name)
1161*4947cdc7SCole Faust
1162*4947cdc7SCole Faust	case *syntax.ForStmt:
1163*4947cdc7SCole Faust		// Keep consistent with ForClause.
1164*4947cdc7SCole Faust		head := fcomp.newBlock()
1165*4947cdc7SCole Faust		body := fcomp.newBlock()
1166*4947cdc7SCole Faust		tail := fcomp.newBlock()
1167*4947cdc7SCole Faust
1168*4947cdc7SCole Faust		fcomp.expr(stmt.X)
1169*4947cdc7SCole Faust		fcomp.setPos(stmt.For)
1170*4947cdc7SCole Faust		fcomp.emit(ITERPUSH)
1171*4947cdc7SCole Faust		fcomp.jump(head)
1172*4947cdc7SCole Faust
1173*4947cdc7SCole Faust		fcomp.block = head
1174*4947cdc7SCole Faust		fcomp.condjump(ITERJMP, tail, body)
1175*4947cdc7SCole Faust
1176*4947cdc7SCole Faust		fcomp.block = body
1177*4947cdc7SCole Faust		fcomp.assign(stmt.For, stmt.Vars)
1178*4947cdc7SCole Faust		fcomp.loops = append(fcomp.loops, loop{break_: tail, continue_: head})
1179*4947cdc7SCole Faust		fcomp.stmts(stmt.Body)
1180*4947cdc7SCole Faust		fcomp.loops = fcomp.loops[:len(fcomp.loops)-1]
1181*4947cdc7SCole Faust		fcomp.jump(head)
1182*4947cdc7SCole Faust
1183*4947cdc7SCole Faust		fcomp.block = tail
1184*4947cdc7SCole Faust		fcomp.emit(ITERPOP)
1185*4947cdc7SCole Faust
1186*4947cdc7SCole Faust	case *syntax.WhileStmt:
1187*4947cdc7SCole Faust		head := fcomp.newBlock()
1188*4947cdc7SCole Faust		body := fcomp.newBlock()
1189*4947cdc7SCole Faust		done := fcomp.newBlock()
1190*4947cdc7SCole Faust
1191*4947cdc7SCole Faust		fcomp.jump(head)
1192*4947cdc7SCole Faust		fcomp.block = head
1193*4947cdc7SCole Faust		fcomp.ifelse(stmt.Cond, body, done)
1194*4947cdc7SCole Faust
1195*4947cdc7SCole Faust		fcomp.block = body
1196*4947cdc7SCole Faust		fcomp.loops = append(fcomp.loops, loop{break_: done, continue_: head})
1197*4947cdc7SCole Faust		fcomp.stmts(stmt.Body)
1198*4947cdc7SCole Faust		fcomp.loops = fcomp.loops[:len(fcomp.loops)-1]
1199*4947cdc7SCole Faust		fcomp.jump(head)
1200*4947cdc7SCole Faust
1201*4947cdc7SCole Faust		fcomp.block = done
1202*4947cdc7SCole Faust
1203*4947cdc7SCole Faust	case *syntax.ReturnStmt:
1204*4947cdc7SCole Faust		if stmt.Result != nil {
1205*4947cdc7SCole Faust			fcomp.expr(stmt.Result)
1206*4947cdc7SCole Faust		} else {
1207*4947cdc7SCole Faust			fcomp.emit(NONE)
1208*4947cdc7SCole Faust		}
1209*4947cdc7SCole Faust		fcomp.emit(RETURN)
1210*4947cdc7SCole Faust		fcomp.block = fcomp.newBlock() // dead code
1211*4947cdc7SCole Faust
1212*4947cdc7SCole Faust	case *syntax.LoadStmt:
1213*4947cdc7SCole Faust		for i := range stmt.From {
1214*4947cdc7SCole Faust			fcomp.string(stmt.From[i].Name)
1215*4947cdc7SCole Faust		}
1216*4947cdc7SCole Faust		module := stmt.Module.Value.(string)
1217*4947cdc7SCole Faust		fcomp.pcomp.prog.Loads = append(fcomp.pcomp.prog.Loads, Binding{
1218*4947cdc7SCole Faust			Name: module,
1219*4947cdc7SCole Faust			Pos:  stmt.Module.TokenPos,
1220*4947cdc7SCole Faust		})
1221*4947cdc7SCole Faust		fcomp.string(module)
1222*4947cdc7SCole Faust		fcomp.setPos(stmt.Load)
1223*4947cdc7SCole Faust		fcomp.emit1(LOAD, uint32(len(stmt.From)))
1224*4947cdc7SCole Faust		for i := range stmt.To {
1225*4947cdc7SCole Faust			fcomp.set(stmt.To[len(stmt.To)-1-i])
1226*4947cdc7SCole Faust		}
1227*4947cdc7SCole Faust
1228*4947cdc7SCole Faust	default:
1229*4947cdc7SCole Faust		start, _ := stmt.Span()
1230*4947cdc7SCole Faust		log.Panicf("%s: exec: unexpected statement %T", start, stmt)
1231*4947cdc7SCole Faust	}
1232*4947cdc7SCole Faust}
1233*4947cdc7SCole Faust
1234*4947cdc7SCole Faust// assign implements lhs = rhs for arbitrary expressions lhs.
1235*4947cdc7SCole Faust// RHS is on top of stack, consumed.
1236*4947cdc7SCole Faustfunc (fcomp *fcomp) assign(pos syntax.Position, lhs syntax.Expr) {
1237*4947cdc7SCole Faust	switch lhs := lhs.(type) {
1238*4947cdc7SCole Faust	case *syntax.ParenExpr:
1239*4947cdc7SCole Faust		// (lhs) = rhs
1240*4947cdc7SCole Faust		fcomp.assign(pos, lhs.X)
1241*4947cdc7SCole Faust
1242*4947cdc7SCole Faust	case *syntax.Ident:
1243*4947cdc7SCole Faust		// x = rhs
1244*4947cdc7SCole Faust		fcomp.set(lhs)
1245*4947cdc7SCole Faust
1246*4947cdc7SCole Faust	case *syntax.TupleExpr:
1247*4947cdc7SCole Faust		// x, y = rhs
1248*4947cdc7SCole Faust		fcomp.assignSequence(pos, lhs.List)
1249*4947cdc7SCole Faust
1250*4947cdc7SCole Faust	case *syntax.ListExpr:
1251*4947cdc7SCole Faust		// [x, y] = rhs
1252*4947cdc7SCole Faust		fcomp.assignSequence(pos, lhs.List)
1253*4947cdc7SCole Faust
1254*4947cdc7SCole Faust	case *syntax.IndexExpr:
1255*4947cdc7SCole Faust		// x[y] = rhs
1256*4947cdc7SCole Faust		fcomp.expr(lhs.X)
1257*4947cdc7SCole Faust		fcomp.emit(EXCH)
1258*4947cdc7SCole Faust		fcomp.expr(lhs.Y)
1259*4947cdc7SCole Faust		fcomp.emit(EXCH)
1260*4947cdc7SCole Faust		fcomp.setPos(lhs.Lbrack)
1261*4947cdc7SCole Faust		fcomp.emit(SETINDEX)
1262*4947cdc7SCole Faust
1263*4947cdc7SCole Faust	case *syntax.DotExpr:
1264*4947cdc7SCole Faust		// x.f = rhs
1265*4947cdc7SCole Faust		fcomp.expr(lhs.X)
1266*4947cdc7SCole Faust		fcomp.emit(EXCH)
1267*4947cdc7SCole Faust		fcomp.setPos(lhs.Dot)
1268*4947cdc7SCole Faust		fcomp.emit1(SETFIELD, fcomp.pcomp.nameIndex(lhs.Name.Name))
1269*4947cdc7SCole Faust
1270*4947cdc7SCole Faust	default:
1271*4947cdc7SCole Faust		panic(lhs)
1272*4947cdc7SCole Faust	}
1273*4947cdc7SCole Faust}
1274*4947cdc7SCole Faust
1275*4947cdc7SCole Faustfunc (fcomp *fcomp) assignSequence(pos syntax.Position, lhs []syntax.Expr) {
1276*4947cdc7SCole Faust	fcomp.setPos(pos)
1277*4947cdc7SCole Faust	fcomp.emit1(UNPACK, uint32(len(lhs)))
1278*4947cdc7SCole Faust	for i := range lhs {
1279*4947cdc7SCole Faust		fcomp.assign(pos, lhs[i])
1280*4947cdc7SCole Faust	}
1281*4947cdc7SCole Faust}
1282*4947cdc7SCole Faust
1283*4947cdc7SCole Faustfunc (fcomp *fcomp) expr(e syntax.Expr) {
1284*4947cdc7SCole Faust	switch e := e.(type) {
1285*4947cdc7SCole Faust	case *syntax.ParenExpr:
1286*4947cdc7SCole Faust		fcomp.expr(e.X)
1287*4947cdc7SCole Faust
1288*4947cdc7SCole Faust	case *syntax.Ident:
1289*4947cdc7SCole Faust		fcomp.lookup(e)
1290*4947cdc7SCole Faust
1291*4947cdc7SCole Faust	case *syntax.Literal:
1292*4947cdc7SCole Faust		// e.Value is int64, float64, *bigInt, string
1293*4947cdc7SCole Faust		v := e.Value
1294*4947cdc7SCole Faust		if e.Token == syntax.BYTES {
1295*4947cdc7SCole Faust			v = Bytes(v.(string))
1296*4947cdc7SCole Faust		}
1297*4947cdc7SCole Faust		fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(v))
1298*4947cdc7SCole Faust
1299*4947cdc7SCole Faust	case *syntax.ListExpr:
1300*4947cdc7SCole Faust		for _, x := range e.List {
1301*4947cdc7SCole Faust			fcomp.expr(x)
1302*4947cdc7SCole Faust		}
1303*4947cdc7SCole Faust		fcomp.emit1(MAKELIST, uint32(len(e.List)))
1304*4947cdc7SCole Faust
1305*4947cdc7SCole Faust	case *syntax.CondExpr:
1306*4947cdc7SCole Faust		// Keep consistent with IfStmt.
1307*4947cdc7SCole Faust		t := fcomp.newBlock()
1308*4947cdc7SCole Faust		f := fcomp.newBlock()
1309*4947cdc7SCole Faust		done := fcomp.newBlock()
1310*4947cdc7SCole Faust
1311*4947cdc7SCole Faust		fcomp.ifelse(e.Cond, t, f)
1312*4947cdc7SCole Faust
1313*4947cdc7SCole Faust		fcomp.block = t
1314*4947cdc7SCole Faust		fcomp.expr(e.True)
1315*4947cdc7SCole Faust		fcomp.jump(done)
1316*4947cdc7SCole Faust
1317*4947cdc7SCole Faust		fcomp.block = f
1318*4947cdc7SCole Faust		fcomp.expr(e.False)
1319*4947cdc7SCole Faust		fcomp.jump(done)
1320*4947cdc7SCole Faust
1321*4947cdc7SCole Faust		fcomp.block = done
1322*4947cdc7SCole Faust
1323*4947cdc7SCole Faust	case *syntax.IndexExpr:
1324*4947cdc7SCole Faust		fcomp.expr(e.X)
1325*4947cdc7SCole Faust		fcomp.expr(e.Y)
1326*4947cdc7SCole Faust		fcomp.setPos(e.Lbrack)
1327*4947cdc7SCole Faust		fcomp.emit(INDEX)
1328*4947cdc7SCole Faust
1329*4947cdc7SCole Faust	case *syntax.SliceExpr:
1330*4947cdc7SCole Faust		fcomp.setPos(e.Lbrack)
1331*4947cdc7SCole Faust		fcomp.expr(e.X)
1332*4947cdc7SCole Faust		if e.Lo != nil {
1333*4947cdc7SCole Faust			fcomp.expr(e.Lo)
1334*4947cdc7SCole Faust		} else {
1335*4947cdc7SCole Faust			fcomp.emit(NONE)
1336*4947cdc7SCole Faust		}
1337*4947cdc7SCole Faust		if e.Hi != nil {
1338*4947cdc7SCole Faust			fcomp.expr(e.Hi)
1339*4947cdc7SCole Faust		} else {
1340*4947cdc7SCole Faust			fcomp.emit(NONE)
1341*4947cdc7SCole Faust		}
1342*4947cdc7SCole Faust		if e.Step != nil {
1343*4947cdc7SCole Faust			fcomp.expr(e.Step)
1344*4947cdc7SCole Faust		} else {
1345*4947cdc7SCole Faust			fcomp.emit(NONE)
1346*4947cdc7SCole Faust		}
1347*4947cdc7SCole Faust		fcomp.emit(SLICE)
1348*4947cdc7SCole Faust
1349*4947cdc7SCole Faust	case *syntax.Comprehension:
1350*4947cdc7SCole Faust		if e.Curly {
1351*4947cdc7SCole Faust			fcomp.emit(MAKEDICT)
1352*4947cdc7SCole Faust		} else {
1353*4947cdc7SCole Faust			fcomp.emit1(MAKELIST, 0)
1354*4947cdc7SCole Faust		}
1355*4947cdc7SCole Faust		fcomp.comprehension(e, 0)
1356*4947cdc7SCole Faust
1357*4947cdc7SCole Faust	case *syntax.TupleExpr:
1358*4947cdc7SCole Faust		fcomp.tuple(e.List)
1359*4947cdc7SCole Faust
1360*4947cdc7SCole Faust	case *syntax.DictExpr:
1361*4947cdc7SCole Faust		fcomp.emit(MAKEDICT)
1362*4947cdc7SCole Faust		for _, entry := range e.List {
1363*4947cdc7SCole Faust			entry := entry.(*syntax.DictEntry)
1364*4947cdc7SCole Faust			fcomp.emit(DUP)
1365*4947cdc7SCole Faust			fcomp.expr(entry.Key)
1366*4947cdc7SCole Faust			fcomp.expr(entry.Value)
1367*4947cdc7SCole Faust			fcomp.setPos(entry.Colon)
1368*4947cdc7SCole Faust			fcomp.emit(SETDICTUNIQ)
1369*4947cdc7SCole Faust		}
1370*4947cdc7SCole Faust
1371*4947cdc7SCole Faust	case *syntax.UnaryExpr:
1372*4947cdc7SCole Faust		fcomp.expr(e.X)
1373*4947cdc7SCole Faust		fcomp.setPos(e.OpPos)
1374*4947cdc7SCole Faust		switch e.Op {
1375*4947cdc7SCole Faust		case syntax.MINUS:
1376*4947cdc7SCole Faust			fcomp.emit(UMINUS)
1377*4947cdc7SCole Faust		case syntax.PLUS:
1378*4947cdc7SCole Faust			fcomp.emit(UPLUS)
1379*4947cdc7SCole Faust		case syntax.NOT:
1380*4947cdc7SCole Faust			fcomp.emit(NOT)
1381*4947cdc7SCole Faust		case syntax.TILDE:
1382*4947cdc7SCole Faust			fcomp.emit(TILDE)
1383*4947cdc7SCole Faust		default:
1384*4947cdc7SCole Faust			log.Panicf("%s: unexpected unary op: %s", e.OpPos, e.Op)
1385*4947cdc7SCole Faust		}
1386*4947cdc7SCole Faust
1387*4947cdc7SCole Faust	case *syntax.BinaryExpr:
1388*4947cdc7SCole Faust		switch e.Op {
1389*4947cdc7SCole Faust		// short-circuit operators
1390*4947cdc7SCole Faust		// TODO(adonovan): use ifelse to simplify conditions.
1391*4947cdc7SCole Faust		case syntax.OR:
1392*4947cdc7SCole Faust			// x or y  =>  if x then x else y
1393*4947cdc7SCole Faust			done := fcomp.newBlock()
1394*4947cdc7SCole Faust			y := fcomp.newBlock()
1395*4947cdc7SCole Faust
1396*4947cdc7SCole Faust			fcomp.expr(e.X)
1397*4947cdc7SCole Faust			fcomp.emit(DUP)
1398*4947cdc7SCole Faust			fcomp.condjump(CJMP, done, y)
1399*4947cdc7SCole Faust
1400*4947cdc7SCole Faust			fcomp.block = y
1401*4947cdc7SCole Faust			fcomp.emit(POP) // discard X
1402*4947cdc7SCole Faust			fcomp.expr(e.Y)
1403*4947cdc7SCole Faust			fcomp.jump(done)
1404*4947cdc7SCole Faust
1405*4947cdc7SCole Faust			fcomp.block = done
1406*4947cdc7SCole Faust
1407*4947cdc7SCole Faust		case syntax.AND:
1408*4947cdc7SCole Faust			// x and y  =>  if x then y else x
1409*4947cdc7SCole Faust			done := fcomp.newBlock()
1410*4947cdc7SCole Faust			y := fcomp.newBlock()
1411*4947cdc7SCole Faust
1412*4947cdc7SCole Faust			fcomp.expr(e.X)
1413*4947cdc7SCole Faust			fcomp.emit(DUP)
1414*4947cdc7SCole Faust			fcomp.condjump(CJMP, y, done)
1415*4947cdc7SCole Faust
1416*4947cdc7SCole Faust			fcomp.block = y
1417*4947cdc7SCole Faust			fcomp.emit(POP) // discard X
1418*4947cdc7SCole Faust			fcomp.expr(e.Y)
1419*4947cdc7SCole Faust			fcomp.jump(done)
1420*4947cdc7SCole Faust
1421*4947cdc7SCole Faust			fcomp.block = done
1422*4947cdc7SCole Faust
1423*4947cdc7SCole Faust		case syntax.PLUS:
1424*4947cdc7SCole Faust			fcomp.plus(e)
1425*4947cdc7SCole Faust
1426*4947cdc7SCole Faust		default:
1427*4947cdc7SCole Faust			// all other strict binary operator (includes comparisons)
1428*4947cdc7SCole Faust			fcomp.expr(e.X)
1429*4947cdc7SCole Faust			fcomp.expr(e.Y)
1430*4947cdc7SCole Faust			fcomp.binop(e.OpPos, e.Op)
1431*4947cdc7SCole Faust		}
1432*4947cdc7SCole Faust
1433*4947cdc7SCole Faust	case *syntax.DotExpr:
1434*4947cdc7SCole Faust		fcomp.expr(e.X)
1435*4947cdc7SCole Faust		fcomp.setPos(e.Dot)
1436*4947cdc7SCole Faust		fcomp.emit1(ATTR, fcomp.pcomp.nameIndex(e.Name.Name))
1437*4947cdc7SCole Faust
1438*4947cdc7SCole Faust	case *syntax.CallExpr:
1439*4947cdc7SCole Faust		fcomp.call(e)
1440*4947cdc7SCole Faust
1441*4947cdc7SCole Faust	case *syntax.LambdaExpr:
1442*4947cdc7SCole Faust		fcomp.function(e.Function.(*resolve.Function))
1443*4947cdc7SCole Faust
1444*4947cdc7SCole Faust	default:
1445*4947cdc7SCole Faust		start, _ := e.Span()
1446*4947cdc7SCole Faust		log.Panicf("%s: unexpected expr %T", start, e)
1447*4947cdc7SCole Faust	}
1448*4947cdc7SCole Faust}
1449*4947cdc7SCole Faust
1450*4947cdc7SCole Fausttype summand struct {
1451*4947cdc7SCole Faust	x       syntax.Expr
1452*4947cdc7SCole Faust	plusPos syntax.Position
1453*4947cdc7SCole Faust}
1454*4947cdc7SCole Faust
1455*4947cdc7SCole Faust// plus emits optimized code for ((a+b)+...)+z that avoids naive
1456*4947cdc7SCole Faust// quadratic behavior for strings, tuples, and lists,
1457*4947cdc7SCole Faust// and folds together adjacent literals of the same type.
1458*4947cdc7SCole Faustfunc (fcomp *fcomp) plus(e *syntax.BinaryExpr) {
1459*4947cdc7SCole Faust	// Gather all the right operands of the left tree of plusses.
1460*4947cdc7SCole Faust	// A tree (((a+b)+c)+d) becomes args=[a +b +c +d].
1461*4947cdc7SCole Faust	args := make([]summand, 0, 2) // common case: 2 operands
1462*4947cdc7SCole Faust	for plus := e; ; {
1463*4947cdc7SCole Faust		args = append(args, summand{unparen(plus.Y), plus.OpPos})
1464*4947cdc7SCole Faust		left := unparen(plus.X)
1465*4947cdc7SCole Faust		x, ok := left.(*syntax.BinaryExpr)
1466*4947cdc7SCole Faust		if !ok || x.Op != syntax.PLUS {
1467*4947cdc7SCole Faust			args = append(args, summand{x: left})
1468*4947cdc7SCole Faust			break
1469*4947cdc7SCole Faust		}
1470*4947cdc7SCole Faust		plus = x
1471*4947cdc7SCole Faust	}
1472*4947cdc7SCole Faust	// Reverse args to syntactic order.
1473*4947cdc7SCole Faust	for i, n := 0, len(args)/2; i < n; i++ {
1474*4947cdc7SCole Faust		j := len(args) - 1 - i
1475*4947cdc7SCole Faust		args[i], args[j] = args[j], args[i]
1476*4947cdc7SCole Faust	}
1477*4947cdc7SCole Faust
1478*4947cdc7SCole Faust	// Fold sums of adjacent literals of the same type: ""+"", []+[], ()+().
1479*4947cdc7SCole Faust	out := args[:0] // compact in situ
1480*4947cdc7SCole Faust	for i := 0; i < len(args); {
1481*4947cdc7SCole Faust		j := i + 1
1482*4947cdc7SCole Faust		if code := addable(args[i].x); code != 0 {
1483*4947cdc7SCole Faust			for j < len(args) && addable(args[j].x) == code {
1484*4947cdc7SCole Faust				j++
1485*4947cdc7SCole Faust			}
1486*4947cdc7SCole Faust			if j > i+1 {
1487*4947cdc7SCole Faust				args[i].x = add(code, args[i:j])
1488*4947cdc7SCole Faust			}
1489*4947cdc7SCole Faust		}
1490*4947cdc7SCole Faust		out = append(out, args[i])
1491*4947cdc7SCole Faust		i = j
1492*4947cdc7SCole Faust	}
1493*4947cdc7SCole Faust	args = out
1494*4947cdc7SCole Faust
1495*4947cdc7SCole Faust	// Emit code for an n-ary sum (n > 0).
1496*4947cdc7SCole Faust	fcomp.expr(args[0].x)
1497*4947cdc7SCole Faust	for _, summand := range args[1:] {
1498*4947cdc7SCole Faust		fcomp.expr(summand.x)
1499*4947cdc7SCole Faust		fcomp.setPos(summand.plusPos)
1500*4947cdc7SCole Faust		fcomp.emit(PLUS)
1501*4947cdc7SCole Faust	}
1502*4947cdc7SCole Faust
1503*4947cdc7SCole Faust	// If len(args) > 2, use of an accumulator instead of a chain of
1504*4947cdc7SCole Faust	// PLUS operations may be more efficient.
1505*4947cdc7SCole Faust	// However, no gain was measured on a workload analogous to Bazel loading;
1506*4947cdc7SCole Faust	// TODO(adonovan): opt: re-evaluate on a Bazel analysis-like workload.
1507*4947cdc7SCole Faust	//
1508*4947cdc7SCole Faust	// We cannot use a single n-ary SUM operation
1509*4947cdc7SCole Faust	//    a b c SUM<3>
1510*4947cdc7SCole Faust	// because we need to report a distinct error for each
1511*4947cdc7SCole Faust	// individual '+' operation, so three additional operations are
1512*4947cdc7SCole Faust	// needed:
1513*4947cdc7SCole Faust	//
1514*4947cdc7SCole Faust	//   ACCSTART => create buffer and append to it
1515*4947cdc7SCole Faust	//   ACCUM    => append to buffer
1516*4947cdc7SCole Faust	//   ACCEND   => get contents of buffer
1517*4947cdc7SCole Faust	//
1518*4947cdc7SCole Faust	// For string, list, and tuple values, the interpreter can
1519*4947cdc7SCole Faust	// optimize these operations by using a mutable buffer.
1520*4947cdc7SCole Faust	// For all other types, ACCSTART and ACCEND would behave like
1521*4947cdc7SCole Faust	// the identity function and ACCUM behaves like PLUS.
1522*4947cdc7SCole Faust	// ACCUM must correctly support user-defined operations
1523*4947cdc7SCole Faust	// such as list+foo.
1524*4947cdc7SCole Faust	//
1525*4947cdc7SCole Faust	// fcomp.emit(ACCSTART)
1526*4947cdc7SCole Faust	// for _, summand := range args[1:] {
1527*4947cdc7SCole Faust	// 	fcomp.expr(summand.x)
1528*4947cdc7SCole Faust	// 	fcomp.setPos(summand.plusPos)
1529*4947cdc7SCole Faust	// 	fcomp.emit(ACCUM)
1530*4947cdc7SCole Faust	// }
1531*4947cdc7SCole Faust	// fcomp.emit(ACCEND)
1532*4947cdc7SCole Faust}
1533*4947cdc7SCole Faust
1534*4947cdc7SCole Faust// addable reports whether e is a statically addable
1535*4947cdc7SCole Faust// expression: a [s]tring, [b]ytes, [l]ist, or [t]uple.
1536*4947cdc7SCole Faustfunc addable(e syntax.Expr) rune {
1537*4947cdc7SCole Faust	switch e := e.(type) {
1538*4947cdc7SCole Faust	case *syntax.Literal:
1539*4947cdc7SCole Faust		// TODO(adonovan): opt: support INT/FLOAT/BIGINT constant folding.
1540*4947cdc7SCole Faust		switch e.Token {
1541*4947cdc7SCole Faust		case syntax.STRING:
1542*4947cdc7SCole Faust			return 's'
1543*4947cdc7SCole Faust		case syntax.BYTES:
1544*4947cdc7SCole Faust			return 'b'
1545*4947cdc7SCole Faust		}
1546*4947cdc7SCole Faust	case *syntax.ListExpr:
1547*4947cdc7SCole Faust		return 'l'
1548*4947cdc7SCole Faust	case *syntax.TupleExpr:
1549*4947cdc7SCole Faust		return 't'
1550*4947cdc7SCole Faust	}
1551*4947cdc7SCole Faust	return 0
1552*4947cdc7SCole Faust}
1553*4947cdc7SCole Faust
1554*4947cdc7SCole Faust// add returns an expression denoting the sum of args,
1555*4947cdc7SCole Faust// which are all addable values of the type indicated by code.
1556*4947cdc7SCole Faust// The resulting syntax is degenerate, lacking position, etc.
1557*4947cdc7SCole Faustfunc add(code rune, args []summand) syntax.Expr {
1558*4947cdc7SCole Faust	switch code {
1559*4947cdc7SCole Faust	case 's', 'b':
1560*4947cdc7SCole Faust		var buf strings.Builder
1561*4947cdc7SCole Faust		for _, arg := range args {
1562*4947cdc7SCole Faust			buf.WriteString(arg.x.(*syntax.Literal).Value.(string))
1563*4947cdc7SCole Faust		}
1564*4947cdc7SCole Faust		tok := syntax.STRING
1565*4947cdc7SCole Faust		if code == 'b' {
1566*4947cdc7SCole Faust			tok = syntax.BYTES
1567*4947cdc7SCole Faust		}
1568*4947cdc7SCole Faust		return &syntax.Literal{Token: tok, Value: buf.String()}
1569*4947cdc7SCole Faust	case 'l':
1570*4947cdc7SCole Faust		var elems []syntax.Expr
1571*4947cdc7SCole Faust		for _, arg := range args {
1572*4947cdc7SCole Faust			elems = append(elems, arg.x.(*syntax.ListExpr).List...)
1573*4947cdc7SCole Faust		}
1574*4947cdc7SCole Faust		return &syntax.ListExpr{List: elems}
1575*4947cdc7SCole Faust	case 't':
1576*4947cdc7SCole Faust		var elems []syntax.Expr
1577*4947cdc7SCole Faust		for _, arg := range args {
1578*4947cdc7SCole Faust			elems = append(elems, arg.x.(*syntax.TupleExpr).List...)
1579*4947cdc7SCole Faust		}
1580*4947cdc7SCole Faust		return &syntax.TupleExpr{List: elems}
1581*4947cdc7SCole Faust	}
1582*4947cdc7SCole Faust	panic(code)
1583*4947cdc7SCole Faust}
1584*4947cdc7SCole Faust
1585*4947cdc7SCole Faustfunc unparen(e syntax.Expr) syntax.Expr {
1586*4947cdc7SCole Faust	if p, ok := e.(*syntax.ParenExpr); ok {
1587*4947cdc7SCole Faust		return unparen(p.X)
1588*4947cdc7SCole Faust	}
1589*4947cdc7SCole Faust	return e
1590*4947cdc7SCole Faust}
1591*4947cdc7SCole Faust
1592*4947cdc7SCole Faustfunc (fcomp *fcomp) binop(pos syntax.Position, op syntax.Token) {
1593*4947cdc7SCole Faust	// TODO(adonovan): simplify by assuming syntax and compiler constants align.
1594*4947cdc7SCole Faust	fcomp.setPos(pos)
1595*4947cdc7SCole Faust	switch op {
1596*4947cdc7SCole Faust	// arithmetic
1597*4947cdc7SCole Faust	case syntax.PLUS:
1598*4947cdc7SCole Faust		fcomp.emit(PLUS)
1599*4947cdc7SCole Faust	case syntax.MINUS:
1600*4947cdc7SCole Faust		fcomp.emit(MINUS)
1601*4947cdc7SCole Faust	case syntax.STAR:
1602*4947cdc7SCole Faust		fcomp.emit(STAR)
1603*4947cdc7SCole Faust	case syntax.SLASH:
1604*4947cdc7SCole Faust		fcomp.emit(SLASH)
1605*4947cdc7SCole Faust	case syntax.SLASHSLASH:
1606*4947cdc7SCole Faust		fcomp.emit(SLASHSLASH)
1607*4947cdc7SCole Faust	case syntax.PERCENT:
1608*4947cdc7SCole Faust		fcomp.emit(PERCENT)
1609*4947cdc7SCole Faust	case syntax.AMP:
1610*4947cdc7SCole Faust		fcomp.emit(AMP)
1611*4947cdc7SCole Faust	case syntax.PIPE:
1612*4947cdc7SCole Faust		fcomp.emit(PIPE)
1613*4947cdc7SCole Faust	case syntax.CIRCUMFLEX:
1614*4947cdc7SCole Faust		fcomp.emit(CIRCUMFLEX)
1615*4947cdc7SCole Faust	case syntax.LTLT:
1616*4947cdc7SCole Faust		fcomp.emit(LTLT)
1617*4947cdc7SCole Faust	case syntax.GTGT:
1618*4947cdc7SCole Faust		fcomp.emit(GTGT)
1619*4947cdc7SCole Faust	case syntax.IN:
1620*4947cdc7SCole Faust		fcomp.emit(IN)
1621*4947cdc7SCole Faust	case syntax.NOT_IN:
1622*4947cdc7SCole Faust		fcomp.emit(IN)
1623*4947cdc7SCole Faust		fcomp.emit(NOT)
1624*4947cdc7SCole Faust
1625*4947cdc7SCole Faust		// comparisons
1626*4947cdc7SCole Faust	case syntax.EQL,
1627*4947cdc7SCole Faust		syntax.NEQ,
1628*4947cdc7SCole Faust		syntax.GT,
1629*4947cdc7SCole Faust		syntax.LT,
1630*4947cdc7SCole Faust		syntax.LE,
1631*4947cdc7SCole Faust		syntax.GE:
1632*4947cdc7SCole Faust		fcomp.emit(Opcode(op-syntax.EQL) + EQL)
1633*4947cdc7SCole Faust
1634*4947cdc7SCole Faust	default:
1635*4947cdc7SCole Faust		log.Panicf("%s: unexpected binary op: %s", pos, op)
1636*4947cdc7SCole Faust	}
1637*4947cdc7SCole Faust}
1638*4947cdc7SCole Faust
1639*4947cdc7SCole Faustfunc (fcomp *fcomp) call(call *syntax.CallExpr) {
1640*4947cdc7SCole Faust	// TODO(adonovan): opt: Use optimized path for calling methods
1641*4947cdc7SCole Faust	// of built-ins: x.f(...) to avoid materializing a closure.
1642*4947cdc7SCole Faust	// if dot, ok := call.Fcomp.(*syntax.DotExpr); ok {
1643*4947cdc7SCole Faust	// 	fcomp.expr(dot.X)
1644*4947cdc7SCole Faust	// 	fcomp.args(call)
1645*4947cdc7SCole Faust	// 	fcomp.emit1(CALL_ATTR, fcomp.name(dot.Name.Name))
1646*4947cdc7SCole Faust	// 	return
1647*4947cdc7SCole Faust	// }
1648*4947cdc7SCole Faust
1649*4947cdc7SCole Faust	// usual case
1650*4947cdc7SCole Faust	fcomp.expr(call.Fn)
1651*4947cdc7SCole Faust	op, arg := fcomp.args(call)
1652*4947cdc7SCole Faust	fcomp.setPos(call.Lparen)
1653*4947cdc7SCole Faust	fcomp.emit1(op, arg)
1654*4947cdc7SCole Faust}
1655*4947cdc7SCole Faust
1656*4947cdc7SCole Faust// args emits code to push a tuple of positional arguments
1657*4947cdc7SCole Faust// and a tuple of named arguments containing alternating keys and values.
1658*4947cdc7SCole Faust// Either or both tuples may be empty (TODO(adonovan): optimize).
1659*4947cdc7SCole Faustfunc (fcomp *fcomp) args(call *syntax.CallExpr) (op Opcode, arg uint32) {
1660*4947cdc7SCole Faust	var callmode int
1661*4947cdc7SCole Faust	// Compute the number of each kind of parameter.
1662*4947cdc7SCole Faust	var p, n int // number of  positional, named arguments
1663*4947cdc7SCole Faust	var varargs, kwargs syntax.Expr
1664*4947cdc7SCole Faust	for _, arg := range call.Args {
1665*4947cdc7SCole Faust		if binary, ok := arg.(*syntax.BinaryExpr); ok && binary.Op == syntax.EQ {
1666*4947cdc7SCole Faust
1667*4947cdc7SCole Faust			// named argument (name, value)
1668*4947cdc7SCole Faust			fcomp.string(binary.X.(*syntax.Ident).Name)
1669*4947cdc7SCole Faust			fcomp.expr(binary.Y)
1670*4947cdc7SCole Faust			n++
1671*4947cdc7SCole Faust			continue
1672*4947cdc7SCole Faust		}
1673*4947cdc7SCole Faust		if unary, ok := arg.(*syntax.UnaryExpr); ok {
1674*4947cdc7SCole Faust			if unary.Op == syntax.STAR {
1675*4947cdc7SCole Faust				callmode |= 1
1676*4947cdc7SCole Faust				varargs = unary.X
1677*4947cdc7SCole Faust				continue
1678*4947cdc7SCole Faust			} else if unary.Op == syntax.STARSTAR {
1679*4947cdc7SCole Faust				callmode |= 2
1680*4947cdc7SCole Faust				kwargs = unary.X
1681*4947cdc7SCole Faust				continue
1682*4947cdc7SCole Faust			}
1683*4947cdc7SCole Faust		}
1684*4947cdc7SCole Faust
1685*4947cdc7SCole Faust		// positional argument
1686*4947cdc7SCole Faust		fcomp.expr(arg)
1687*4947cdc7SCole Faust		p++
1688*4947cdc7SCole Faust	}
1689*4947cdc7SCole Faust
1690*4947cdc7SCole Faust	// Python2 and Python3 both permit named arguments
1691*4947cdc7SCole Faust	// to appear both before and after a *args argument:
1692*4947cdc7SCole Faust	//   f(1, 2, x=3, *[4], y=5, **dict(z=6))
1693*4947cdc7SCole Faust	//
1694*4947cdc7SCole Faust	// They also differ in their evaluation order:
1695*4947cdc7SCole Faust	//  Python2: 1 2 3 5 4 6 (*args and **kwargs evaluated last)
1696*4947cdc7SCole Faust	//  Python3: 1 2 4 3 5 6 (positional args evaluated before named args)
1697*4947cdc7SCole Faust	// Starlark-in-Java historically used a third order:
1698*4947cdc7SCole Faust	//  Lexical: 1 2 3 4 5 6 (all args evaluated left-to-right)
1699*4947cdc7SCole Faust	//
1700*4947cdc7SCole Faust	// After discussion in github.com/bazelbuild/starlark#13, the
1701*4947cdc7SCole Faust	// spec now requires Starlark to statically reject named
1702*4947cdc7SCole Faust	// arguments after *args (e.g. y=5), and to use Python2-style
1703*4947cdc7SCole Faust	// evaluation order. This is both easy to implement and
1704*4947cdc7SCole Faust	// consistent with lexical order:
1705*4947cdc7SCole Faust	//
1706*4947cdc7SCole Faust	//   f(1, 2, x=3, *[4], **dict(z=6)) # 1 2 3 4 6
1707*4947cdc7SCole Faust
1708*4947cdc7SCole Faust	// *args
1709*4947cdc7SCole Faust	if varargs != nil {
1710*4947cdc7SCole Faust		fcomp.expr(varargs)
1711*4947cdc7SCole Faust	}
1712*4947cdc7SCole Faust
1713*4947cdc7SCole Faust	// **kwargs
1714*4947cdc7SCole Faust	if kwargs != nil {
1715*4947cdc7SCole Faust		fcomp.expr(kwargs)
1716*4947cdc7SCole Faust	}
1717*4947cdc7SCole Faust
1718*4947cdc7SCole Faust	// TODO(adonovan): avoid this with a more flexible encoding.
1719*4947cdc7SCole Faust	if p >= 256 || n >= 256 {
1720*4947cdc7SCole Faust		// resolve already checked this; should be unreachable
1721*4947cdc7SCole Faust		panic("too many arguments in call")
1722*4947cdc7SCole Faust	}
1723*4947cdc7SCole Faust
1724*4947cdc7SCole Faust	return CALL + Opcode(callmode), uint32(p<<8 | n)
1725*4947cdc7SCole Faust}
1726*4947cdc7SCole Faust
1727*4947cdc7SCole Faustfunc (fcomp *fcomp) tuple(elems []syntax.Expr) {
1728*4947cdc7SCole Faust	for _, elem := range elems {
1729*4947cdc7SCole Faust		fcomp.expr(elem)
1730*4947cdc7SCole Faust	}
1731*4947cdc7SCole Faust	fcomp.emit1(MAKETUPLE, uint32(len(elems)))
1732*4947cdc7SCole Faust}
1733*4947cdc7SCole Faust
1734*4947cdc7SCole Faustfunc (fcomp *fcomp) comprehension(comp *syntax.Comprehension, clauseIndex int) {
1735*4947cdc7SCole Faust	if clauseIndex == len(comp.Clauses) {
1736*4947cdc7SCole Faust		fcomp.emit(DUP) // accumulator
1737*4947cdc7SCole Faust		if comp.Curly {
1738*4947cdc7SCole Faust			// dict: {k:v for ...}
1739*4947cdc7SCole Faust			// Parser ensures that body is of form k:v.
1740*4947cdc7SCole Faust			// Python-style set comprehensions {body for vars in x}
1741*4947cdc7SCole Faust			// are not supported.
1742*4947cdc7SCole Faust			entry := comp.Body.(*syntax.DictEntry)
1743*4947cdc7SCole Faust			fcomp.expr(entry.Key)
1744*4947cdc7SCole Faust			fcomp.expr(entry.Value)
1745*4947cdc7SCole Faust			fcomp.setPos(entry.Colon)
1746*4947cdc7SCole Faust			fcomp.emit(SETDICT)
1747*4947cdc7SCole Faust		} else {
1748*4947cdc7SCole Faust			// list: [body for vars in x]
1749*4947cdc7SCole Faust			fcomp.expr(comp.Body)
1750*4947cdc7SCole Faust			fcomp.emit(APPEND)
1751*4947cdc7SCole Faust		}
1752*4947cdc7SCole Faust		return
1753*4947cdc7SCole Faust	}
1754*4947cdc7SCole Faust
1755*4947cdc7SCole Faust	clause := comp.Clauses[clauseIndex]
1756*4947cdc7SCole Faust	switch clause := clause.(type) {
1757*4947cdc7SCole Faust	case *syntax.IfClause:
1758*4947cdc7SCole Faust		t := fcomp.newBlock()
1759*4947cdc7SCole Faust		done := fcomp.newBlock()
1760*4947cdc7SCole Faust		fcomp.ifelse(clause.Cond, t, done)
1761*4947cdc7SCole Faust
1762*4947cdc7SCole Faust		fcomp.block = t
1763*4947cdc7SCole Faust		fcomp.comprehension(comp, clauseIndex+1)
1764*4947cdc7SCole Faust		fcomp.jump(done)
1765*4947cdc7SCole Faust
1766*4947cdc7SCole Faust		fcomp.block = done
1767*4947cdc7SCole Faust		return
1768*4947cdc7SCole Faust
1769*4947cdc7SCole Faust	case *syntax.ForClause:
1770*4947cdc7SCole Faust		// Keep consistent with ForStmt.
1771*4947cdc7SCole Faust		head := fcomp.newBlock()
1772*4947cdc7SCole Faust		body := fcomp.newBlock()
1773*4947cdc7SCole Faust		tail := fcomp.newBlock()
1774*4947cdc7SCole Faust
1775*4947cdc7SCole Faust		fcomp.expr(clause.X)
1776*4947cdc7SCole Faust		fcomp.setPos(clause.For)
1777*4947cdc7SCole Faust		fcomp.emit(ITERPUSH)
1778*4947cdc7SCole Faust		fcomp.jump(head)
1779*4947cdc7SCole Faust
1780*4947cdc7SCole Faust		fcomp.block = head
1781*4947cdc7SCole Faust		fcomp.condjump(ITERJMP, tail, body)
1782*4947cdc7SCole Faust
1783*4947cdc7SCole Faust		fcomp.block = body
1784*4947cdc7SCole Faust		fcomp.assign(clause.For, clause.Vars)
1785*4947cdc7SCole Faust		fcomp.comprehension(comp, clauseIndex+1)
1786*4947cdc7SCole Faust		fcomp.jump(head)
1787*4947cdc7SCole Faust
1788*4947cdc7SCole Faust		fcomp.block = tail
1789*4947cdc7SCole Faust		fcomp.emit(ITERPOP)
1790*4947cdc7SCole Faust		return
1791*4947cdc7SCole Faust	}
1792*4947cdc7SCole Faust
1793*4947cdc7SCole Faust	start, _ := clause.Span()
1794*4947cdc7SCole Faust	log.Panicf("%s: unexpected comprehension clause %T", start, clause)
1795*4947cdc7SCole Faust}
1796*4947cdc7SCole Faust
1797*4947cdc7SCole Faustfunc (fcomp *fcomp) function(f *resolve.Function) {
1798*4947cdc7SCole Faust	// Evaluation of the defaults may fail, so record the position.
1799*4947cdc7SCole Faust	fcomp.setPos(f.Pos)
1800*4947cdc7SCole Faust
1801*4947cdc7SCole Faust	// To reduce allocation, we emit a combined tuple
1802*4947cdc7SCole Faust	// for the defaults and the freevars.
1803*4947cdc7SCole Faust	// The function knows where to split it at run time.
1804*4947cdc7SCole Faust
1805*4947cdc7SCole Faust	// Generate tuple of parameter defaults. For:
1806*4947cdc7SCole Faust	//  def f(p1, p2=dp2, p3=dp3, *, k1, k2=dk2, k3, **kwargs)
1807*4947cdc7SCole Faust	// the tuple is:
1808*4947cdc7SCole Faust	//  (dp2, dp3, MANDATORY, dk2, MANDATORY).
1809*4947cdc7SCole Faust	ndefaults := 0
1810*4947cdc7SCole Faust	seenStar := false
1811*4947cdc7SCole Faust	for _, param := range f.Params {
1812*4947cdc7SCole Faust		switch param := param.(type) {
1813*4947cdc7SCole Faust		case *syntax.BinaryExpr:
1814*4947cdc7SCole Faust			fcomp.expr(param.Y)
1815*4947cdc7SCole Faust			ndefaults++
1816*4947cdc7SCole Faust		case *syntax.UnaryExpr:
1817*4947cdc7SCole Faust			seenStar = true // * or *args (also **kwargs)
1818*4947cdc7SCole Faust		case *syntax.Ident:
1819*4947cdc7SCole Faust			if seenStar {
1820*4947cdc7SCole Faust				fcomp.emit(MANDATORY)
1821*4947cdc7SCole Faust				ndefaults++
1822*4947cdc7SCole Faust			}
1823*4947cdc7SCole Faust		}
1824*4947cdc7SCole Faust	}
1825*4947cdc7SCole Faust
1826*4947cdc7SCole Faust	// Capture the cells of the function's
1827*4947cdc7SCole Faust	// free variables from the lexical environment.
1828*4947cdc7SCole Faust	for _, freevar := range f.FreeVars {
1829*4947cdc7SCole Faust		// Don't call fcomp.lookup because we want
1830*4947cdc7SCole Faust		// the cell itself, not its content.
1831*4947cdc7SCole Faust		switch freevar.Scope {
1832*4947cdc7SCole Faust		case resolve.Free:
1833*4947cdc7SCole Faust			fcomp.emit1(FREE, uint32(freevar.Index))
1834*4947cdc7SCole Faust		case resolve.Cell:
1835*4947cdc7SCole Faust			fcomp.emit1(LOCAL, uint32(freevar.Index))
1836*4947cdc7SCole Faust		}
1837*4947cdc7SCole Faust	}
1838*4947cdc7SCole Faust
1839*4947cdc7SCole Faust	fcomp.emit1(MAKETUPLE, uint32(ndefaults+len(f.FreeVars)))
1840*4947cdc7SCole Faust
1841*4947cdc7SCole Faust	funcode := fcomp.pcomp.function(f.Name, f.Pos, f.Body, f.Locals, f.FreeVars)
1842*4947cdc7SCole Faust
1843*4947cdc7SCole Faust	if debug {
1844*4947cdc7SCole Faust		// TODO(adonovan): do compilations sequentially not as a tree,
1845*4947cdc7SCole Faust		// to make the log easier to read.
1846*4947cdc7SCole Faust		// Simplify by identifying Toplevel and functionIndex 0.
1847*4947cdc7SCole Faust		fmt.Fprintf(os.Stderr, "resuming %s @ %s\n", fcomp.fn.Name, fcomp.pos)
1848*4947cdc7SCole Faust	}
1849*4947cdc7SCole Faust
1850*4947cdc7SCole Faust	// def f(a, *, b=1) has only 2 parameters.
1851*4947cdc7SCole Faust	numParams := len(f.Params)
1852*4947cdc7SCole Faust	if f.NumKwonlyParams > 0 && !f.HasVarargs {
1853*4947cdc7SCole Faust		numParams--
1854*4947cdc7SCole Faust	}
1855*4947cdc7SCole Faust
1856*4947cdc7SCole Faust	funcode.NumParams = numParams
1857*4947cdc7SCole Faust	funcode.NumKwonlyParams = f.NumKwonlyParams
1858*4947cdc7SCole Faust	funcode.HasVarargs = f.HasVarargs
1859*4947cdc7SCole Faust	funcode.HasKwargs = f.HasKwargs
1860*4947cdc7SCole Faust	fcomp.emit1(MAKEFUNC, fcomp.pcomp.functionIndex(funcode))
1861*4947cdc7SCole Faust}
1862*4947cdc7SCole Faust
1863*4947cdc7SCole Faust// ifelse emits a Boolean control flow decision.
1864*4947cdc7SCole Faust// On return, the current block is unset.
1865*4947cdc7SCole Faustfunc (fcomp *fcomp) ifelse(cond syntax.Expr, t, f *block) {
1866*4947cdc7SCole Faust	switch cond := cond.(type) {
1867*4947cdc7SCole Faust	case *syntax.UnaryExpr:
1868*4947cdc7SCole Faust		if cond.Op == syntax.NOT {
1869*4947cdc7SCole Faust			// if not x then goto t else goto f
1870*4947cdc7SCole Faust			//    =>
1871*4947cdc7SCole Faust			// if x then goto f else goto t
1872*4947cdc7SCole Faust			fcomp.ifelse(cond.X, f, t)
1873*4947cdc7SCole Faust			return
1874*4947cdc7SCole Faust		}
1875*4947cdc7SCole Faust
1876*4947cdc7SCole Faust	case *syntax.BinaryExpr:
1877*4947cdc7SCole Faust		switch cond.Op {
1878*4947cdc7SCole Faust		case syntax.AND:
1879*4947cdc7SCole Faust			// if x and y then goto t else goto f
1880*4947cdc7SCole Faust			//    =>
1881*4947cdc7SCole Faust			// if x then ifelse(y, t, f) else goto f
1882*4947cdc7SCole Faust			fcomp.expr(cond.X)
1883*4947cdc7SCole Faust			y := fcomp.newBlock()
1884*4947cdc7SCole Faust			fcomp.condjump(CJMP, y, f)
1885*4947cdc7SCole Faust
1886*4947cdc7SCole Faust			fcomp.block = y
1887*4947cdc7SCole Faust			fcomp.ifelse(cond.Y, t, f)
1888*4947cdc7SCole Faust			return
1889*4947cdc7SCole Faust
1890*4947cdc7SCole Faust		case syntax.OR:
1891*4947cdc7SCole Faust			// if x or y then goto t else goto f
1892*4947cdc7SCole Faust			//    =>
1893*4947cdc7SCole Faust			// if x then goto t else ifelse(y, t, f)
1894*4947cdc7SCole Faust			fcomp.expr(cond.X)
1895*4947cdc7SCole Faust			y := fcomp.newBlock()
1896*4947cdc7SCole Faust			fcomp.condjump(CJMP, t, y)
1897*4947cdc7SCole Faust
1898*4947cdc7SCole Faust			fcomp.block = y
1899*4947cdc7SCole Faust			fcomp.ifelse(cond.Y, t, f)
1900*4947cdc7SCole Faust			return
1901*4947cdc7SCole Faust		case syntax.NOT_IN:
1902*4947cdc7SCole Faust			// if x not in y then goto t else goto f
1903*4947cdc7SCole Faust			//    =>
1904*4947cdc7SCole Faust			// if x in y then goto f else goto t
1905*4947cdc7SCole Faust			copy := *cond
1906*4947cdc7SCole Faust			copy.Op = syntax.IN
1907*4947cdc7SCole Faust			fcomp.expr(&copy)
1908*4947cdc7SCole Faust			fcomp.condjump(CJMP, f, t)
1909*4947cdc7SCole Faust			return
1910*4947cdc7SCole Faust		}
1911*4947cdc7SCole Faust	}
1912*4947cdc7SCole Faust
1913*4947cdc7SCole Faust	// general case
1914*4947cdc7SCole Faust	fcomp.expr(cond)
1915*4947cdc7SCole Faust	fcomp.condjump(CJMP, t, f)
1916*4947cdc7SCole Faust}
1917