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(©) 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