github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/compiler/codegen/optimize.go (about)

     1  package codegen
     2  
     3  import (
     4  	"github.com/hirochachacha/plua/compiler/ast"
     5  	"github.com/hirochachacha/plua/compiler/token"
     6  	"github.com/hirochachacha/plua/internal/arith"
     7  	"github.com/hirochachacha/plua/object"
     8  	"github.com/hirochachacha/plua/opcode"
     9  )
    10  
    11  // constant folding
    12  
    13  func (g *generator) foldExpr(expr ast.Expr) (val object.Value, ok bool) {
    14  	if skipConstantFolding {
    15  		return nil, false
    16  	}
    17  
    18  	switch expr := expr.(type) {
    19  	case *ast.ParenExpr:
    20  		return g.foldExpr(expr.X)
    21  	case *ast.BasicLit:
    22  		return g.foldBasic(expr)
    23  	case *ast.UnaryExpr:
    24  		return g.foldUnary(expr)
    25  	case *ast.BinaryExpr:
    26  		return g.foldBinary(expr)
    27  	}
    28  
    29  	return nil, false
    30  }
    31  
    32  func (g *generator) foldBasic(expr *ast.BasicLit) (val object.Value, ok bool) {
    33  	if skipConstantFolding {
    34  		return nil, false
    35  	}
    36  
    37  	g.tokLine = expr.Pos().Line
    38  
    39  	if c, ok := g.cfolds[expr]; ok {
    40  		return c, true
    41  	}
    42  
    43  	tok := expr.Token
    44  
    45  	switch tok.Type {
    46  	case token.NIL:
    47  		val = nil
    48  	case token.FALSE:
    49  		val = object.False
    50  	case token.TRUE:
    51  		val = object.True
    52  	case token.INT:
    53  		if i, ok := g.parseInteger(tok, false); ok {
    54  			val = i
    55  		} else {
    56  			val = g.parseNumber(tok, false)
    57  		}
    58  	case token.FLOAT:
    59  		val = g.parseNumber(tok, false)
    60  	case token.STRING:
    61  		val = object.String(g.unquoteString(tok))
    62  	default:
    63  		panic("unreachable")
    64  	}
    65  
    66  	g.cfolds[expr] = val
    67  
    68  	return val, true
    69  }
    70  
    71  func (g *generator) foldUnary(expr *ast.UnaryExpr) (val object.Value, ok bool) {
    72  	if skipConstantFolding {
    73  		return nil, false
    74  	}
    75  
    76  	g.tokLine = expr.Pos().Line
    77  
    78  	if c, ok := g.cfolds[expr]; ok {
    79  		return c, c != nil
    80  	}
    81  
    82  	if expr.Op == token.UNM {
    83  		if x, ok := expr.X.(*ast.BasicLit); ok {
    84  			tok := x.Token
    85  
    86  			switch tok.Type {
    87  			case token.INT:
    88  				var val object.Value
    89  
    90  				if i, ok := g.parseInteger(tok, true); ok {
    91  					val = i
    92  				} else {
    93  					val = g.parseNumber(tok, true)
    94  				}
    95  
    96  				g.cfolds[expr] = val
    97  
    98  				return val, true
    99  			case token.FLOAT:
   100  				val := g.parseNumber(tok, true)
   101  
   102  				g.cfolds[expr] = val
   103  
   104  				return val, true
   105  			}
   106  		}
   107  	}
   108  
   109  	switch expr.Op {
   110  	case token.UNM:
   111  		if x, ok := g.foldExpr(expr.X); ok {
   112  			if unm := arith.Unm(x); unm != nil {
   113  				val = unm
   114  			}
   115  		}
   116  	case token.BNOT:
   117  		if x, ok := g.foldExpr(expr.X); ok {
   118  			if bnot := arith.Bnot(x); bnot != nil {
   119  				val = bnot
   120  			}
   121  		}
   122  	case token.NOT:
   123  		if x, ok := g.foldExpr(expr.X); ok {
   124  			if not := arith.Not(x); not != nil {
   125  				val = not
   126  			}
   127  		}
   128  	case token.LEN:
   129  		x := expr.X
   130  
   131  		for {
   132  			paren, ok := x.(*ast.ParenExpr)
   133  			if !ok {
   134  				break
   135  			}
   136  			x = paren.X
   137  		}
   138  
   139  		switch x := x.(type) {
   140  		case *ast.BasicLit:
   141  			tok := x.Token
   142  			if tok.Type == token.STRING {
   143  				val = object.Integer(len(g.unquoteString(tok)))
   144  			}
   145  		case *ast.TableLit:
   146  			var a []ast.Expr
   147  			for _, e := range x.Fields {
   148  				if _, ok := e.(*ast.KeyValueExpr); !ok {
   149  					if _, ok := g.foldExpr(e); !ok {
   150  						g.cfolds[expr] = nil
   151  
   152  						return nil, false
   153  					}
   154  					a = append(a, e)
   155  				}
   156  			}
   157  
   158  			for len(a) > 0 {
   159  				if lit, ok := a[len(a)-1].(*ast.BasicLit); ok && lit.Token.Type == token.NIL {
   160  					a = a[:len(a)-1]
   161  
   162  					continue
   163  				}
   164  
   165  				break
   166  			}
   167  
   168  			val = object.Integer(len(a))
   169  		}
   170  	default:
   171  		panic("unreachable")
   172  	}
   173  
   174  	g.cfolds[expr] = val
   175  
   176  	return val, val != nil
   177  }
   178  
   179  func (g *generator) foldBinary(expr *ast.BinaryExpr) (val object.Value, ok bool) {
   180  	if skipConstantFolding {
   181  		return nil, false
   182  	}
   183  
   184  	g.tokLine = expr.Pos().Line
   185  
   186  	if c, ok := g.cfolds[expr]; ok {
   187  		return c, c != nil
   188  	}
   189  
   190  	switch expr.Op {
   191  	case token.EQ:
   192  		if x, ok := g.foldExpr(expr.X); ok {
   193  			if y, ok := g.foldExpr(expr.Y); ok {
   194  				if b := arith.Equal(x, y); b != nil {
   195  					val = b
   196  				}
   197  			}
   198  		}
   199  	case token.NE:
   200  		if x, ok := g.foldExpr(expr.X); ok {
   201  			if y, ok := g.foldExpr(expr.Y); ok {
   202  				if b := arith.NotEqual(x, y); b != nil {
   203  					val = b
   204  				}
   205  			}
   206  		}
   207  	case token.LT:
   208  		if x, ok := g.foldExpr(expr.X); ok {
   209  			if y, ok := g.foldExpr(expr.Y); ok {
   210  				if b := arith.LessThan(x, y); b != nil {
   211  					val = b
   212  				}
   213  			}
   214  		}
   215  	case token.LE:
   216  		if x, ok := g.foldExpr(expr.X); ok {
   217  			if y, ok := g.foldExpr(expr.Y); ok {
   218  				if b := arith.LessThanOrEqualTo(x, y); b != nil {
   219  					val = b
   220  				}
   221  			}
   222  		}
   223  	case token.GT:
   224  		if x, ok := g.foldExpr(expr.X); ok {
   225  			if y, ok := g.foldExpr(expr.Y); ok {
   226  				if b := arith.LessThan(y, x); b != nil {
   227  					val = b
   228  				}
   229  			}
   230  		}
   231  	case token.GE:
   232  		if x, ok := g.foldExpr(expr.X); ok {
   233  			if y, ok := g.foldExpr(expr.Y); ok {
   234  				if b := arith.LessThanOrEqualTo(y, x); b != nil {
   235  					val = b
   236  				}
   237  			}
   238  		}
   239  
   240  	case token.ADD:
   241  		if x, ok := g.foldExpr(expr.X); ok {
   242  			if y, ok := g.foldExpr(expr.Y); ok {
   243  				if sum := arith.Add(x, y); sum != nil {
   244  					val = sum
   245  				}
   246  			}
   247  		}
   248  	case token.SUB:
   249  		if x, ok := g.foldExpr(expr.X); ok {
   250  			if y, ok := g.foldExpr(expr.Y); ok {
   251  				if diff := arith.Sub(x, y); diff != nil {
   252  					val = diff
   253  				}
   254  			}
   255  		}
   256  	case token.MUL:
   257  		if x, ok := g.foldExpr(expr.X); ok {
   258  			if y, ok := g.foldExpr(expr.Y); ok {
   259  				if prod := arith.Mul(x, y); prod != nil {
   260  					val = prod
   261  				}
   262  			}
   263  		}
   264  	case token.MOD:
   265  		if x, ok := g.foldExpr(expr.X); ok {
   266  			if y, ok := g.foldExpr(expr.Y); ok {
   267  				if rem, _ := arith.Mod(x, y); rem != nil {
   268  					val = rem
   269  				}
   270  			}
   271  		}
   272  	case token.POW:
   273  		if x, ok := g.foldExpr(expr.X); ok {
   274  			if y, ok := g.foldExpr(expr.Y); ok {
   275  				if prod := arith.Pow(x, y); prod != nil {
   276  					val = prod
   277  				}
   278  			}
   279  		}
   280  	case token.DIV:
   281  		if x, ok := g.foldExpr(expr.X); ok {
   282  			if y, ok := g.foldExpr(expr.Y); ok {
   283  				if quo := arith.Div(x, y); quo != nil {
   284  					val = quo
   285  				}
   286  			}
   287  		}
   288  	case token.IDIV:
   289  		if x, ok := g.foldExpr(expr.X); ok {
   290  			if y, ok := g.foldExpr(expr.Y); ok {
   291  				if quo, _ := arith.Idiv(x, y); quo != nil {
   292  					val = quo
   293  				}
   294  			}
   295  		}
   296  	case token.BAND:
   297  		if x, ok := g.foldExpr(expr.X); ok {
   298  			if y, ok := g.foldExpr(expr.Y); ok {
   299  				if band := arith.Band(x, y); band != nil {
   300  					val = band
   301  				}
   302  			}
   303  		}
   304  	case token.BOR:
   305  		if x, ok := g.foldExpr(expr.X); ok {
   306  			if y, ok := g.foldExpr(expr.Y); ok {
   307  				if bor := arith.Bor(x, y); bor != nil {
   308  					val = bor
   309  				}
   310  			}
   311  		}
   312  	case token.BXOR:
   313  		if x, ok := g.foldExpr(expr.X); ok {
   314  			if y, ok := g.foldExpr(expr.Y); ok {
   315  				if bxor := arith.Bxor(x, y); bxor != nil {
   316  					val = bxor
   317  				}
   318  			}
   319  		}
   320  	case token.SHL:
   321  		if x, ok := g.foldExpr(expr.X); ok {
   322  			if y, ok := g.foldExpr(expr.Y); ok {
   323  				if shl := arith.Shl(x, y); shl != nil {
   324  					val = shl
   325  				}
   326  			}
   327  		}
   328  	case token.SHR:
   329  		if x, ok := g.foldExpr(expr.X); ok {
   330  			if y, ok := g.foldExpr(expr.Y); ok {
   331  				if shr := arith.Shr(x, y); shr != nil {
   332  					val = shr
   333  				}
   334  			}
   335  		}
   336  	case token.CONCAT:
   337  		if x, ok := g.foldExpr(expr.X); ok {
   338  			if y, ok := g.foldExpr(expr.Y); ok {
   339  				if con := arith.Concat(x, y); con != nil {
   340  					val = con
   341  				}
   342  			}
   343  		}
   344  	case token.AND:
   345  		if x, ok := g.foldExpr(expr.X); ok {
   346  			if object.ToBoolean(x) {
   347  				return g.foldExpr(expr.Y)
   348  			}
   349  
   350  			return x, true
   351  		}
   352  	case token.OR:
   353  		if x, ok := g.foldExpr(expr.X); ok {
   354  			if object.ToBoolean(x) {
   355  				return x, true
   356  			}
   357  
   358  			return g.foldExpr(expr.Y)
   359  		}
   360  	default:
   361  		panic("unreachable")
   362  	}
   363  
   364  	g.cfolds[expr] = val
   365  
   366  	return val, val != nil
   367  }
   368  
   369  // peep hole optimization
   370  
   371  // optimize load to same address twice
   372  func (g *generator) peepLoad(i0 opcode.Instruction, i opcode.Instruction) bool {
   373  	a := i.A()
   374  
   375  	switch i0.OpCode() {
   376  	case opcode.MOVE, opcode.LOADK, opcode.GETUPVAL, opcode.NEWTABLE, opcode.CLOSURE:
   377  
   378  		a0 := i0.A()
   379  
   380  		if a0 == a { // local x = any; local x = 1 => local x = 1
   381  			return true
   382  		}
   383  	case opcode.LOADBOOL:
   384  		c0 := i0.C()
   385  
   386  		if c0 == 0 {
   387  			a0 := i0.A()
   388  
   389  			if a0 == a { // local x = true; local x = 1 => local x = 1
   390  				return true
   391  			}
   392  		}
   393  	case opcode.LOADNIL:
   394  		a0 := i0.A()
   395  		b0 := i0.B()
   396  
   397  		if a0 == a && b0 == 0 { // local x = nil; local x = 1 => local x = 1
   398  			return true
   399  		}
   400  	}
   401  
   402  	return false
   403  }
   404  
   405  func (g *generator) peepLine(i opcode.Instruction, line int) {
   406  	var offset int
   407  	var i0 opcode.Instruction
   408  
   409  	for {
   410  		if len(g.Code) > offset {
   411  			i0 = g.Code[len(g.Code)-offset-1]
   412  			op0 := i0.OpCode()
   413  
   414  			switch op := i.OpCode(); op {
   415  			case opcode.LOADNIL:
   416  				a := i.A()
   417  				b := i.B()
   418  
   419  				switch op0 {
   420  				case opcode.MOVE, opcode.LOADK, opcode.GETUPVAL, opcode.NEWTABLE, opcode.CLOSURE:
   421  					a0 := i0.A()
   422  
   423  					if a <= a0 && a0 <= a+b { // local x = any; x = nil => local x = nil
   424  						offset++
   425  
   426  						continue
   427  					}
   428  				case opcode.LOADBOOL:
   429  					c0 := i0.C()
   430  
   431  					if c0 == 0 {
   432  						a0 := i0.A()
   433  
   434  						if a <= a0 && a0 <= a+b { // local x = true; x = nil => local x = nil
   435  							offset++
   436  
   437  							continue
   438  						}
   439  					}
   440  				case opcode.LOADNIL:
   441  					a0 := i0.A()
   442  					b0 := i0.B()
   443  
   444  					// local x = nil; x = nil => local x = nil
   445  					switch {
   446  					case a0 < a:
   447  						if a <= a0+b0 {
   448  							if a+b > a0+b0 {
   449  								i = opcode.AB(opcode.LOADNIL, a0, a+b-a0)
   450  
   451  								offset++
   452  
   453  								continue
   454  							}
   455  
   456  							g.Code = g.Code[:len(g.Code)-offset]
   457  							g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   458  
   459  							return
   460  						}
   461  
   462  						if a == a0+b0+1 {
   463  							i = opcode.AB(opcode.LOADNIL, a0, a+b-a0)
   464  
   465  							offset++
   466  
   467  							continue
   468  						}
   469  
   470  						if a == a0+1 {
   471  							i = opcode.AB(opcode.LOADNIL, a0, a+b-a0)
   472  
   473  							offset++
   474  
   475  							continue
   476  						}
   477  					case a < a0:
   478  						if a0 <= a+b {
   479  							if a+b > a0+b0 {
   480  								offset++
   481  
   482  								continue
   483  							}
   484  
   485  							i = opcode.AB(opcode.LOADNIL, a, a0+b0-a)
   486  
   487  							offset++
   488  
   489  							continue
   490  						}
   491  
   492  						if a0 == a+b+1 {
   493  							i = opcode.AB(opcode.LOADNIL, a, a0+b0-a)
   494  
   495  							offset++
   496  
   497  							continue
   498  						}
   499  
   500  						if a0 == a+1 {
   501  							i = opcode.AB(opcode.LOADNIL, a, a0+b0-a)
   502  
   503  							offset++
   504  
   505  							continue
   506  						}
   507  					default: // a == a0
   508  						if b > b0 {
   509  							offset++
   510  
   511  							continue
   512  						}
   513  
   514  						g.Code = g.Code[:len(g.Code)-offset]
   515  						g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   516  
   517  						return
   518  					}
   519  				}
   520  			case opcode.MOVE:
   521  				a := i.A()
   522  				b := i.B()
   523  
   524  				if a == b { // local x = local x => none
   525  					g.Code = g.Code[:len(g.Code)-offset]
   526  					g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   527  
   528  					return
   529  				}
   530  
   531  				if g.peepLoad(i0, i) {
   532  					offset++
   533  
   534  					continue
   535  				}
   536  
   537  				switch op0 {
   538  				case opcode.MOVE:
   539  					a0 := i0.A()
   540  					b0 := i0.B()
   541  
   542  					if a0 == b {
   543  						if b0 == a { // local x = local y; y = x => local x = local y
   544  							g.Code = g.Code[:len(g.Code)-offset]
   545  							g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   546  
   547  							return
   548  						}
   549  
   550  						if !g.locktmp && a0 >= g.nlocals { // tmp x = local y; local z = tmp x => local z = local y
   551  							b0 := i0.B()
   552  
   553  							i = opcode.AB(op0, a, b0)
   554  
   555  							offset++
   556  
   557  							continue
   558  						}
   559  					}
   560  				case opcode.LOADK:
   561  					a0 := i0.A()
   562  
   563  					if !g.locktmp && a0 >= g.nlocals {
   564  						if a0 == b { // tmp x = 1; local z = tmp x => local z = 1
   565  							bx0 := i0.Bx()
   566  
   567  							i = opcode.ABx(op0, a, bx0)
   568  
   569  							offset++
   570  
   571  							continue
   572  						}
   573  					}
   574  				case opcode.LOADBOOL:
   575  					c0 := i0.C()
   576  
   577  					if c0 == 0 {
   578  						a0 := i0.A()
   579  
   580  						if !g.locktmp && a0 >= g.nlocals {
   581  							if a0 == b { // tmp x = true; local z = tmp x => local z = true
   582  								b0 := i0.B()
   583  
   584  								i = opcode.ABC(op0, a, b0, 0)
   585  
   586  								offset++
   587  
   588  								continue
   589  							}
   590  						}
   591  					}
   592  				case opcode.LOADNIL:
   593  					a0 := i0.A()
   594  					b0 := i0.B()
   595  
   596  					if a0 <= b && b <= a0+b0 {
   597  						if a0 <= a && a <= a0+b0 {
   598  							g.Code = g.Code[:len(g.Code)-offset]
   599  							g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   600  
   601  							return
   602  						}
   603  
   604  						if a == a0-1 {
   605  							i = opcode.AB(op0, a, a0+b0-a)
   606  
   607  							offset++
   608  
   609  							continue
   610  						}
   611  
   612  						if a == a0+b0+1 {
   613  							i = opcode.AB(op0, a0, b0+1)
   614  
   615  							offset++
   616  
   617  							continue
   618  						}
   619  
   620  						if !g.locktmp && a0 >= g.nlocals {
   621  							if b0 == 0 { // tmp x = nil; local z = tmp x => local z = nil
   622  								i = opcode.AB(op0, a, 0)
   623  
   624  								offset++
   625  
   626  								continue
   627  							}
   628  						}
   629  					}
   630  				case opcode.GETUPVAL:
   631  					a0 := i0.A()
   632  
   633  					if !g.locktmp && a0 >= g.nlocals {
   634  						if a0 == b { // tmp x = lexical y; local z = tmp x => local z = lexical y
   635  							b0 := i0.B()
   636  
   637  							i = opcode.AB(op0, a, b0)
   638  
   639  							offset++
   640  
   641  							continue
   642  						}
   643  					}
   644  				case opcode.GETTABLE:
   645  					a0 := i0.A()
   646  
   647  					if !g.locktmp && a0 >= g.nlocals {
   648  						if a0 == b { // tmp x = y.foo; local z = tmp x => local z = y.foo
   649  							b0 := i0.B()
   650  							c0 := i0.C()
   651  
   652  							i = opcode.ABC(op0, a, b0, c0)
   653  
   654  							offset++
   655  
   656  							continue
   657  						}
   658  					}
   659  				case opcode.GETTABUP:
   660  					a0 := i0.A()
   661  
   662  					if !g.locktmp && a0 >= g.nlocals {
   663  						if a0 == b { // tmp x = global y; local z = tmp x => local z = global y
   664  							b0 := i0.B()
   665  							c0 := i0.C()
   666  
   667  							i = opcode.ABC(op0, a, b0, c0)
   668  
   669  							offset++
   670  
   671  							continue
   672  						}
   673  					}
   674  				case opcode.NEWTABLE:
   675  					a0 := i0.A()
   676  
   677  					if !g.locktmp && a0 >= g.nlocals {
   678  						if a0 == b { // tmp x = <table>; local z = tmp x => local z = <table>
   679  							b0 := i0.B()
   680  							c0 := i0.C()
   681  
   682  							i = opcode.ABC(op0, a, b0, c0)
   683  
   684  							offset++
   685  
   686  							continue
   687  						}
   688  					}
   689  				case opcode.CLOSURE:
   690  					a0 := i0.A()
   691  
   692  					if !g.locktmp && a0 >= g.nlocals {
   693  						if a0 == b { // tmp x = <func>; local z = tmp x => local z = <func>
   694  							bx0 := i0.Bx()
   695  
   696  							i = opcode.ABx(op0, a, bx0)
   697  
   698  							offset++
   699  
   700  							continue
   701  						}
   702  					}
   703  				case opcode.UNM, opcode.BNOT, opcode.NOT, opcode.LEN:
   704  					a0 := i0.A()
   705  
   706  					if !g.locktmp && a0 >= g.nlocals {
   707  						if a0 == b { // tmp x = - local y; local z = tmp x => local z = - local y
   708  							b0 := i0.B()
   709  
   710  							i = opcode.AB(op0, a, b0)
   711  
   712  							offset++
   713  
   714  							continue
   715  						}
   716  					}
   717  				case opcode.ADD, opcode.SUB, opcode.MUL, opcode.MOD, opcode.POW, opcode.DIV,
   718  					opcode.IDIV, opcode.BAND, opcode.BOR, opcode.BXOR, opcode.SHL, opcode.SHR:
   719  					a0 := i0.A()
   720  
   721  					if !g.locktmp && a0 >= g.nlocals {
   722  						if a0 == b { // tmp x = tmp y + tmp z; local w = tmp x => local w = tmp y + tmp z
   723  							b0 := i0.B()
   724  							c0 := i0.C()
   725  
   726  							i = opcode.ABC(op0, a, b0, c0)
   727  
   728  							offset++
   729  
   730  							continue
   731  						}
   732  					}
   733  				case opcode.CONCAT:
   734  					a0 := i0.A()
   735  
   736  					if !g.locktmp && a0 >= g.nlocals {
   737  						if a0 == b { // tmp x = - local y; local z = tmp x => local z = - local y
   738  							b0 := i0.B()
   739  							c0 := i0.C()
   740  
   741  							i = opcode.ABC(op0, a, b0, c0)
   742  
   743  							offset++
   744  
   745  							continue
   746  						}
   747  					}
   748  				}
   749  			case opcode.LOADBOOL:
   750  				c0 := i0.C()
   751  
   752  				if c0 == 0 {
   753  					if g.peepLoad(i0, i) {
   754  						offset++
   755  
   756  						continue
   757  					}
   758  				}
   759  			case opcode.LOADK, opcode.NEWTABLE, opcode.CLOSURE:
   760  				if g.peepLoad(i0, i) {
   761  					offset++
   762  
   763  					continue
   764  				}
   765  			case opcode.GETUPVAL:
   766  				if g.peepLoad(i0, i) {
   767  					offset++
   768  
   769  					continue
   770  				}
   771  
   772  				a := i.A()
   773  				b := i.B()
   774  
   775  				switch op0 {
   776  				case opcode.SETUPVAL:
   777  					a0 := i0.A()
   778  					b0 := i0.B()
   779  
   780  					if a0 == a && b0 == b { // lexical x = local y; local y = lexical x => lexical x = local y
   781  						g.Code = g.Code[:len(g.Code)-offset]
   782  						g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   783  
   784  						return
   785  					}
   786  				}
   787  			case opcode.GETTABUP:
   788  				a := i.A()
   789  				c := i.C()
   790  
   791  				if a != c {
   792  					if g.peepLoad(i0, i) {
   793  						offset++
   794  
   795  						continue
   796  					}
   797  				}
   798  			case opcode.GETTABLE:
   799  				a := i.A()
   800  				b := i.B()
   801  				c := i.C()
   802  
   803  				if a != b && a != c {
   804  					if g.peepLoad(i0, i) {
   805  						offset++
   806  
   807  						continue
   808  					}
   809  				}
   810  
   811  				switch op0 {
   812  				case opcode.GETUPVAL:
   813  					a0 := i0.A()
   814  
   815  					if a0 == a {
   816  						b0 := i0.B()
   817  
   818  						i = opcode.ABC(opcode.GETTABUP, a, b0, c)
   819  
   820  						offset++
   821  
   822  						continue
   823  					}
   824  
   825  					if !g.locktmp && a0 >= g.nlocals {
   826  						if a0 == b { // tmp x = lexical env; local z = (tmp x).foo => local z = global foo
   827  							b0 := i0.B()
   828  
   829  							i = opcode.ABC(opcode.GETTABUP, a, b0, c)
   830  
   831  							offset++
   832  
   833  							continue
   834  						}
   835  					}
   836  				}
   837  			case opcode.ADD, opcode.SUB, opcode.MUL, opcode.MOD, opcode.POW,
   838  				opcode.DIV, opcode.IDIV, opcode.BAND, opcode.BOR, opcode.BXOR, opcode.SHL, opcode.SHR:
   839  				a := i.A()
   840  				b := i.B()
   841  				c := i.C()
   842  
   843  				if a != b && a != c {
   844  					if g.peepLoad(i0, i) {
   845  						offset++
   846  
   847  						continue
   848  					}
   849  				}
   850  			case opcode.UNM, opcode.BNOT, opcode.NOT, opcode.LEN:
   851  				a := i.A()
   852  				b := i.B()
   853  
   854  				if a != b {
   855  					if g.peepLoad(i0, i) {
   856  						offset++
   857  
   858  						continue
   859  					}
   860  				}
   861  			case opcode.SETUPVAL:
   862  				a := i.A()
   863  				b := i.B()
   864  
   865  				switch op0 {
   866  				case opcode.MOVE:
   867  					a0 := i0.A()
   868  
   869  					if a == a0 {
   870  						if !g.locktmp && a >= g.nlocals {
   871  							b0 := i0.B()
   872  
   873  							i = opcode.AB(opcode.SETUPVAL, b0, b)
   874  
   875  							offset++
   876  
   877  							continue
   878  						}
   879  					}
   880  				case opcode.SETUPVAL:
   881  					b0 := i0.B()
   882  
   883  					if b0 == b { // lexical x = local y; lexical x = local z => lexical x = local z
   884  						offset++
   885  
   886  						continue
   887  					}
   888  				case opcode.GETUPVAL:
   889  					a0 := i0.A()
   890  					b0 := i0.B()
   891  
   892  					if a0 == a && b0 == b { // lexical x = local y; local y = lexical x => lexical x = local y
   893  						g.Code = g.Code[:len(g.Code)-offset]
   894  						g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   895  
   896  						return
   897  					}
   898  				}
   899  			case opcode.SETTABLE:
   900  				a := i.A()
   901  				b := i.B()
   902  				c := i.C()
   903  
   904  				switch op0 {
   905  				case opcode.MOVE:
   906  					a0 := i0.A()
   907  
   908  					if a0 == c {
   909  						a := i.A()
   910  						b := i.B()
   911  						b0 := i0.B()
   912  
   913  						i = opcode.ABC(op, a, b, b0)
   914  
   915  						offset++
   916  
   917  						continue
   918  					}
   919  				case opcode.LOADK:
   920  					a0 := i0.A()
   921  					bx0 := i0.Bx()
   922  
   923  					if bx0 <= opcode.MaxRKIndex && a0 == c {
   924  						a := i.A()
   925  						b := i.B()
   926  
   927  						i = opcode.ABC(op, a, b, bx0|opcode.BitRK)
   928  
   929  						offset++
   930  
   931  						continue
   932  					}
   933  				case opcode.GETUPVAL:
   934  					a0 := i0.A()
   935  
   936  					if !g.locktmp && a0 >= g.nlocals {
   937  						if a0 == a { // tmp x = lexical env;  (tmp x).foo = local z => global foo = local z
   938  							b0 := i0.B()
   939  
   940  							i = opcode.ABC(opcode.SETTABUP, b0, b, c)
   941  
   942  							offset++
   943  
   944  							continue
   945  						}
   946  					}
   947  				}
   948  			case opcode.SETTABUP:
   949  				c := i.C()
   950  
   951  				switch op0 {
   952  				case opcode.MOVE:
   953  					a0 := i0.A()
   954  
   955  					if a0 == c {
   956  						a := i.A()
   957  						b := i.B()
   958  						b0 := i0.B()
   959  
   960  						i = opcode.ABC(op, a, b, b0)
   961  
   962  						offset++
   963  
   964  						continue
   965  					}
   966  				case opcode.LOADK:
   967  					a0 := i0.A()
   968  					bx0 := i0.Bx()
   969  
   970  					if bx0 <= opcode.MaxRKIndex && a0 == c {
   971  						a := i.A()
   972  						b := i.B()
   973  
   974  						i = opcode.ABC(op, a, b, bx0|opcode.BitRK)
   975  
   976  						offset++
   977  
   978  						continue
   979  					}
   980  				}
   981  			case opcode.JMP:
   982  				a := i.A()
   983  				b := i.B()
   984  
   985  				if a == 0 && b == 0 {
   986  					g.Code = g.Code[:len(g.Code)-offset]
   987  					g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset]
   988  
   989  					return
   990  				}
   991  			}
   992  
   993  			if offset == 0 {
   994  				g.Code = append(g.Code, i)
   995  				g.LineInfo = append(g.LineInfo, line)
   996  			} else {
   997  				g.Code[len(g.Code)-offset] = i
   998  				g.LineInfo[len(g.LineInfo)-offset] = line
   999  
  1000  				g.Code = g.Code[:len(g.Code)-offset+1]
  1001  				g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset+1]
  1002  			}
  1003  
  1004  			return
  1005  
  1006  		}
  1007  
  1008  		if offset == 0 {
  1009  			g.Code = append(g.Code, i)
  1010  			g.LineInfo = append(g.LineInfo, line)
  1011  		} else {
  1012  			g.Code[len(g.Code)-offset] = i
  1013  			g.LineInfo[len(g.LineInfo)-offset] = line
  1014  
  1015  			g.Code = g.Code[:len(g.Code)-offset+1]
  1016  			g.LineInfo = g.LineInfo[:len(g.LineInfo)-offset+1]
  1017  		}
  1018  
  1019  		return
  1020  	}
  1021  }