github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/compiler/codegen/codegen.go (about) 1 package codegen 2 3 import ( 4 "fmt" 5 "math" 6 7 "github.com/hirochachacha/plua/compiler/ast" 8 "github.com/hirochachacha/plua/compiler/token" 9 "github.com/hirochachacha/plua/internal/strconv" 10 "github.com/hirochachacha/plua/internal/version" 11 "github.com/hirochachacha/plua/object" 12 "github.com/hirochachacha/plua/opcode" 13 "github.com/hirochachacha/plua/position" 14 ) 15 16 const ( 17 skipPeepholeOptimization = false 18 skipDeadCodeElimination = false 19 skipConstantFolding = false 20 ) 21 22 var tmp ast.Name 23 24 func tmpName(name string) *ast.Name { 25 t := &tmp 26 t.Name = name 27 return t 28 } 29 30 func Generate(f *ast.File) (proto *object.Proto, err error) { 31 g := newGenerator(nil) 32 33 g.Source = f.Filename 34 g.cfolds = make(map[ast.Expr]object.Value) // cache for constant folding 35 36 defer func() { 37 if r := recover(); r != nil { 38 b := r.(bailout) 39 40 err = b.err 41 } 42 }() 43 44 g.genFile(f) 45 46 proto = g.Proto 47 48 return 49 } 50 51 type label struct { 52 pc int 53 sp int 54 pos position.Position 55 } 56 57 type jumpPoint struct { 58 pc int 59 pos position.Position 60 } 61 62 type generator struct { 63 *object.Proto 64 *scope // block scope 65 outer *generator // function scope 66 67 cfolds map[ast.Expr]object.Value // cache for constant folding 68 69 sp int 70 71 pendingJumps map[*scope]map[string]jumpPoint // pending jumps per scopes 72 73 rconstants map[object.Value]int // reverse map of Proto.constants 74 75 tokLine int 76 77 locktmp bool // don't remove tmp variable by peep hole optimization 78 lockpeep bool // don't do peep hole optimization, because here is jump destination 79 } 80 81 type bailout struct { 82 err error 83 } 84 85 func newGenerator(outer *generator) *generator { 86 g := &generator{ 87 Proto: new(object.Proto), 88 outer: outer, 89 pendingJumps: make(map[*scope]map[string]jumpPoint, 0), 90 rconstants: make(map[object.Value]int, 0), 91 } 92 93 if outer != nil { 94 g.Source = outer.Source 95 g.cfolds = outer.cfolds 96 } 97 98 return g 99 } 100 101 func (g *generator) error(pos position.Position, err error) { 102 pos.SourceName = g.Source 103 104 panic(bailout{ 105 err: &Error{ 106 Pos: pos, 107 Err: err, 108 }, 109 }) 110 } 111 112 func (g *generator) pc() int { 113 return len(g.Code) 114 } 115 116 func (g *generator) nextSP() { 117 g.addSP(1) 118 } 119 120 func (g *generator) addSP(i int) { 121 g.sp += i 122 if g.sp > g.MaxStackSize { 123 g.MaxStackSize = g.sp 124 } 125 } 126 127 func (g *generator) setSP(sp int) { 128 g.sp = sp 129 if g.sp > g.MaxStackSize { 130 g.MaxStackSize = g.sp 131 } 132 } 133 134 // local jumps 135 136 func (g *generator) newLabel() label { 137 l := label{pc: g.pc(), sp: g.sp} 138 139 g.lockpeep = true 140 141 return l 142 } 143 144 func (g *generator) genJumpPoint() jumpPoint { 145 return jumpPoint{pc: g.pushTemp()} 146 } 147 148 // backward jump 149 func (g *generator) genJumpTo(label label) { 150 reljmp := label.pc - g.pc() - 1 151 if reljmp > 0 { 152 panic("unexpected") 153 } 154 155 g.pushInst(opcode.AsBx(opcode.JMP, label.sp+1, reljmp)) 156 } 157 158 // forward jump 159 func (g *generator) genJumpFrom(jmp jumpPoint) { 160 reljmp := g.pc() - jmp.pc - 1 161 if reljmp < 0 { 162 panic("unexpected") 163 } 164 165 g.Code[jmp.pc] = opcode.AsBx(opcode.JMP, 0, reljmp) 166 167 g.lockpeep = true 168 } 169 170 // global jumps 171 172 func (g *generator) declareLabel(name string) { 173 g.declareLabelPos(name, position.NoPos) 174 } 175 176 func (g *generator) declareLabelPos(name string, pos position.Position) { 177 g.labels[name] = label{ 178 pc: g.pc(), 179 sp: g.sp, 180 pos: pos, 181 } 182 } 183 184 func (g *generator) genSetJumpPoint(name string, pos position.Position) { 185 g.tokLine = pos.Line 186 187 jmp := g.genJumpPoint() 188 189 jmp.pos = pos 190 191 if jmps, ok := g.pendingJumps[g.scope]; ok { 192 jmps[name] = jmp 193 } else { 194 g.pendingJumps[g.scope] = map[string]jumpPoint{ 195 name: jmp, 196 } 197 } 198 } 199 200 // close pending jumps 201 func (g *generator) closeJumps() { 202 for scope, pendingJumps := range g.pendingJumps { 203 if len(pendingJumps) == 0 { 204 continue 205 } 206 207 for name, jmp := range pendingJumps { 208 label, ok := scope.resolveLabel(name) 209 if !ok { 210 g.error(jmp.pos, fmt.Errorf("unknown label '%s' for jump", name)) 211 } 212 213 for _, locVar := range g.LocVars { 214 if jmp.pc < locVar.StartPC && locVar.StartPC <= label.pc && label.pc < locVar.EndPC { 215 g.error(label.pos, fmt.Errorf("forward jump over local '%s'", locVar.Name)) 216 } 217 } 218 219 reljmp := label.pc - jmp.pc - 1 220 if reljmp >= 0 { // forward jump 221 g.Code[jmp.pc] = opcode.AsBx(opcode.JMP, 0, reljmp) 222 } else { // backward jump 223 g.Code[jmp.pc] = opcode.AsBx(opcode.JMP, label.sp+1, reljmp) 224 } 225 } 226 227 delete(g.pendingJumps, scope) 228 } 229 } 230 231 func (g *generator) resolveName(name *ast.Name) (link, bool) { 232 if g == nil { 233 return link{}, false 234 } 235 236 if l, ok := g.resolveLocal(name.Name); ok { 237 return l, true 238 } 239 240 if up, ok := g.outer.resolveName(name); ok { 241 return g.declareUpvalueName(name, up), true 242 } 243 244 return link{}, false 245 } 246 247 func (g *generator) resolve(name string) (link, bool) { 248 return g.resolveName(tmpName(name)) 249 } 250 251 func (g *generator) declareLocalName(name *ast.Name, sp int) { 252 nlocals := g.scope.nlocals 253 254 if outer := g.scope.outer; outer != nil { 255 nlocals -= outer.nlocals 256 } 257 258 if nlocals >= version.MAXVAR { 259 g.error(name.Pos(), fmt.Errorf("too many local variables (limit is %d)", version.MAXVAR)) 260 } 261 262 locVar := object.LocVar{ 263 Name: name.Name, 264 StartPC: g.pc(), 265 } 266 267 g.LocVars = append(g.LocVars, locVar) 268 269 g.scope.declare(name.Name, link{ 270 kind: linkLocal, 271 index: sp, 272 }) 273 274 g.nlocals++ 275 } 276 277 func (g *generator) declareLocal(name string, sp int) { 278 g.declareLocalName(tmpName(name), sp) 279 } 280 281 func (g *generator) declareEnviron() { 282 u := object.UpvalueDesc{ 283 Name: "_ENV", 284 Instack: true, 285 Index: 0, 286 } 287 288 g.Upvalues = append(g.Upvalues, u) 289 290 g.scope.declare("_ENV", link{ 291 kind: linkUpval, 292 index: 0, 293 }) 294 } 295 296 func (g *generator) declareUpvalueName(name *ast.Name, up link) link { 297 if len(g.Upvalues) >= version.MAXUPVAL { 298 g.error(name.Pos(), fmt.Errorf("too many upvalues (limit is %d)", version.MAXUPVAL)) 299 } 300 301 instack := up.kind == linkLocal 302 303 // mark upvalue whether it should be closed 304 if instack { 305 scope := g.outer.scope 306 307 for { 308 _, ok := scope.symbols[name.Name] 309 if ok { 310 break 311 } 312 313 scope = scope.outer 314 } 315 316 if scope.outer != nil { 317 scope.doClose = true 318 } 319 } 320 321 ud := object.UpvalueDesc{ 322 Name: name.Name, 323 Instack: instack, 324 Index: up.index, 325 } 326 327 g.Upvalues = append(g.Upvalues, ud) 328 329 link := link{ 330 kind: linkUpval, 331 index: len(g.Upvalues) - 1, 332 } 333 334 g.scope.root().declare(name.Name, link) 335 336 return link 337 } 338 339 type negativeZero struct{} 340 341 func (n negativeZero) Type() object.Type { 342 return object.Type(-1) 343 } 344 345 func (n negativeZero) String() string { 346 return "-0.0" 347 } 348 349 func (g *generator) constant(val object.Value) (k int) { 350 key := val 351 352 // a stupid trick for avoiding +0.0 == -0.0 353 if n, ok := val.(object.Number); ok && n == 0 { 354 u := math.Float64bits(float64(n)) 355 if int(u>>63) == 1 { 356 key = negativeZero{} 357 } 358 } 359 360 if k, ok := g.rconstants[key]; ok { 361 return k 362 } 363 364 k = len(g.Constants) 365 366 g.Constants = append(g.Constants, val) 367 368 g.rconstants[key] = k 369 370 return 371 } 372 373 func (g *generator) proto(f *ast.FuncBody, self bool, endLine int) (p int) { 374 generator := newGenerator(g) 375 376 generator.genFuncBody(f, self, endLine) 377 378 g.Protos = append(g.Protos, generator.Proto) 379 380 generator.Proto = nil 381 generator.outer = nil 382 383 return len(g.Protos) - 1 384 } 385 386 func (g *generator) markRK(k int, next bool) (rk int) { 387 if k > opcode.MaxRKIndex { 388 if k > opcode.MaxBx { 389 g.pushInst(opcode.ABx(opcode.LOADKX, g.sp, 0)) 390 g.pushInst(opcode.Ax(opcode.EXTRAARG, k)) 391 } else { 392 g.pushInst(opcode.ABx(opcode.LOADK, g.sp, k)) 393 } 394 395 rk = g.sp 396 397 if next { 398 g.nextSP() 399 } 400 401 return rk 402 } 403 404 return k | opcode.BitRK 405 } 406 407 func (g *generator) newScope() { 408 g.scope = &scope{ 409 symbols: make(map[string]link, 0), 410 labels: make(map[string]label, 0), 411 outer: nil, 412 savedSP: 0, 413 nlocals: 0, 414 } 415 } 416 417 func (g *generator) openScope() { 418 g.scope = &scope{ 419 symbols: make(map[string]link, 0), 420 labels: make(map[string]label, 0), 421 outer: g.scope, 422 savedSP: g.sp, 423 nlocals: g.scope.nlocals, 424 } 425 } 426 427 func (g *generator) closeScope() { 428 nlocals := g.scope.nlocals 429 430 outer := g.scope.outer 431 432 if outer != nil { 433 nlocals -= outer.nlocals 434 } 435 436 endPC := g.pc() 437 438 if nlocals != 0 { 439 for i := len(g.LocVars) - 1; i >= 0; i-- { 440 if g.LocVars[i].EndPC != 0 { 441 continue 442 } 443 444 nlocals-- 445 446 g.LocVars[i].EndPC = endPC 447 448 if nlocals == 0 { 449 break 450 } 451 } 452 } 453 454 g.sp = g.scope.savedSP 455 456 if g.scope.doClose { 457 g.pushInst(opcode.AsBx(opcode.JMP, g.sp+1, 0)) 458 } 459 460 g.scope.endPC = endPC 461 g.scope = outer 462 463 return 464 } 465 466 func (g *generator) pushInst(inst opcode.Instruction) { 467 g.pushInstLine(inst, g.tokLine) 468 } 469 470 func (g *generator) pushInstLine(inst opcode.Instruction, line int) { 471 if !skipPeepholeOptimization && !g.lockpeep { 472 g.peepLine(inst, line) 473 } else { 474 g.Code = append(g.Code, inst) 475 g.LineInfo = append(g.LineInfo, line) 476 } 477 g.lockpeep = false 478 } 479 480 func (g *generator) pushTemp() (pc int) { 481 return g.pushTempLine(g.tokLine) 482 } 483 484 func (g *generator) pushTempLine(line int) (pc int) { 485 pc = g.pc() 486 487 g.Code = append(g.Code, opcode.AsBx(opcode.JMP, 0, 0)) 488 g.LineInfo = append(g.LineInfo, line) 489 490 return 491 } 492 493 func (g *generator) pushReturn() { 494 g.Code = append(g.Code, opcode.AB(opcode.RETURN, 0, 1)) 495 g.LineInfo = append(g.LineInfo, g.LastLineDefined) 496 } 497 498 func (g *generator) unquoteString(tok token.Token) string { 499 us, err := strconv.Unquote(tok.Lit) 500 if err != nil { 501 g.error(tok.Pos, fmt.Errorf("failed to unquote %s, err: %v", tok.Lit, err)) 502 } 503 return us 504 } 505 506 func (g *generator) parseInteger(tok token.Token, negate bool) (ret object.Integer, ok bool) { 507 if negate { 508 tok.Lit = "-" + tok.Lit 509 } 510 511 i, err := strconv.ParseInt(tok.Lit) 512 if err != nil { 513 if err != strconv.ErrRange { 514 g.error(tok.Pos, fmt.Errorf("failed to parse int %s, err: %v", tok.Lit, err)) 515 } 516 517 return 0, false 518 } 519 520 return object.Integer(i), true 521 } 522 523 func (g *generator) parseNumber(tok token.Token, negate bool) object.Number { 524 if negate { 525 tok.Lit = "-" + tok.Lit 526 } 527 528 f, err := strconv.ParseFloat(tok.Lit) 529 if err != nil { 530 if err != strconv.ErrRange { 531 g.error(tok.Pos, fmt.Errorf("failed to parse float %s, err: %v", tok.Lit, err)) 532 } 533 } 534 535 return object.Number(f) 536 }