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  }