github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/ir/instructions.go (about)

     1  package ir
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/arnodel/golua/ops"
     8  )
     9  
    10  // Instruction is the interface that all ir instruction types must implement.
    11  type Instruction interface {
    12  	fmt.Stringer
    13  	ProcessInstr(InstrProcessor)
    14  }
    15  
    16  // SetRegInstruction is the interface that instructions that set the value of a
    17  // register must implement.
    18  type SetRegInstruction interface {
    19  	DestReg() Register
    20  	WithDestReg(Register) Instruction
    21  }
    22  
    23  // An InstrProcessor can process any instruction.
    24  type InstrProcessor interface {
    25  
    26  	// Real instructions
    27  	ProcessCombineInstr(Combine)
    28  	ProcessTransformInstr(Transform)
    29  	ProcessLoadConstInstr(LoadConst)
    30  	ProcessPushInstr(Push)
    31  	ProcessJumpInstr(Jump)
    32  	ProcessJumpIfInstr(JumpIf)
    33  	ProcessCallInstr(Call)
    34  	ProcessMkClosureInstr(MkClosure)
    35  	ProcessMkContInstr(MkCont)
    36  	ProcessClearRegInstr(ClearReg)
    37  	ProcessMkTableInstr(MkTable)
    38  	ProcessLookupInstr(Lookup)
    39  	ProcessSetIndexInstr(SetIndex)
    40  	ProcessReceiveInstr(Receive)
    41  	ProcessReceiveEtcInstr(ReceiveEtc)
    42  	ProcessEtcLookupInstr(EtcLookup)
    43  	ProcessFillTableInstr(FillTable)
    44  	ProcessTruncateCloseStackInstr(TruncateCloseStack)
    45  	ProcessPushCloseStackInstr(PushCloseStack)
    46  	ProcessPrepForLoopInstr(PrepForLoop)
    47  	ProcessAdvForLoopInstr(AdvForLoop)
    48  
    49  	// These are hints that a register is needed or no longer needed.
    50  	ProcessTakeRegisterInstr(TakeRegister)
    51  	ProcessReleaseRegisterInstr(ReleaseRegister)
    52  
    53  	// A label (for jumping to)
    54  	ProcessDeclareLabelInstr(DeclareLabel)
    55  }
    56  
    57  // A Register is an IR register.  The number of IR registers is not bounded
    58  // (other than the bounds of the underling type).
    59  type Register uint
    60  
    61  func (r Register) String() string {
    62  	return fmt.Sprintf("r%d", r)
    63  }
    64  
    65  // A Label is a location in the IR code.
    66  type Label uint
    67  
    68  func (l Label) String() string {
    69  	return fmt.Sprintf("L%d", l)
    70  }
    71  
    72  // Combine applies the binary operator Op to Lsrc and Rsrc and stores the result
    73  // in Dst.
    74  type Combine struct {
    75  	Op   ops.Op   // Operator to apply to Lsrc and Rsrc
    76  	Dst  Register // Destination register
    77  	Lsrc Register // Left operand register
    78  	Rsrc Register // Right operand register
    79  }
    80  
    81  // DestReg returns the destination register of this instruction.
    82  func (c Combine) DestReg() Register {
    83  	return c.Dst
    84  }
    85  
    86  // WithDestReg returns the same isntruction with a new destination register.
    87  func (c Combine) WithDestReg(r Register) Instruction {
    88  	c.Dst = r
    89  	return c
    90  }
    91  
    92  // ProcessInstr makes the InstrProcessor process this instruction.
    93  func (c Combine) ProcessInstr(p InstrProcessor) {
    94  	p.ProcessCombineInstr(c)
    95  }
    96  
    97  func (c Combine) String() string {
    98  	return fmt.Sprintf("%s := %s(%s, %s)", c.Dst, c.Op, c.Lsrc, c.Rsrc)
    99  }
   100  
   101  // Transform applies a unary operator Op to Src and stores the result in Dst.
   102  type Transform struct {
   103  	Op  ops.Op   // Operator to apply to Src
   104  	Dst Register // Destination register
   105  	Src Register // Operand register
   106  }
   107  
   108  // DestReg returns the destination register of this instruction.
   109  func (t Transform) DestReg() Register {
   110  	return t.Dst
   111  }
   112  
   113  // WithDestReg returns the same isntruction with a new destination register.
   114  func (t Transform) WithDestReg(r Register) Instruction {
   115  	t.Dst = r
   116  	return t
   117  }
   118  
   119  // ProcessInstr makes the InstrProcessor process this instruction.
   120  func (t Transform) ProcessInstr(p InstrProcessor) {
   121  	p.ProcessTransformInstr(t)
   122  }
   123  
   124  func (t Transform) String() string {
   125  	return fmt.Sprintf("%s := %s(%s)", t.Dst, t.Op, t.Src)
   126  }
   127  
   128  // LoadConst loads a constant into a register.
   129  type LoadConst struct {
   130  	Dst  Register // Destination register
   131  	Kidx uint     // Index of the constant to load
   132  }
   133  
   134  // DestReg returns the destination register of this instruction.
   135  func (l LoadConst) DestReg() Register {
   136  	return l.Dst
   137  }
   138  
   139  // WithDestReg returns the same isntruction with a new destination register.
   140  func (l LoadConst) WithDestReg(r Register) Instruction {
   141  	l.Dst = r
   142  	return l
   143  }
   144  
   145  // ProcessInstr makes the InstrProcessor process this instruction.
   146  func (l LoadConst) ProcessInstr(p InstrProcessor) {
   147  	p.ProcessLoadConstInstr(l)
   148  }
   149  
   150  func (l LoadConst) String() string {
   151  	return fmt.Sprintf("%s := k%d", l.Dst, l.Kidx)
   152  }
   153  
   154  // Push pushes the contents of a register into a continuation.
   155  type Push struct {
   156  	Cont Register // Destination register (should contain a continuation)
   157  	Item Register // Register containing item to push
   158  	Etc  bool     // True if the Item is an etc value.
   159  }
   160  
   161  // ProcessInstr makes the InstrProcessor process this instruction.
   162  func (p Push) ProcessInstr(ip InstrProcessor) {
   163  	ip.ProcessPushInstr(p)
   164  }
   165  
   166  func (p Push) String() string {
   167  	return fmt.Sprintf("push %s to %s", p.Item, p.Cont)
   168  }
   169  
   170  // Jump jumps to the givel label.
   171  type Jump struct {
   172  	Label Label
   173  }
   174  
   175  func (j Jump) String() string {
   176  	return fmt.Sprintf("jump %s", j.Label)
   177  }
   178  
   179  // ProcessInstr makes the InstrProcessor process this instruction.
   180  func (j Jump) ProcessInstr(p InstrProcessor) {
   181  	p.ProcessJumpInstr(j)
   182  }
   183  
   184  // JumpIf jumps to the given label if the boolean value in Cond is different from Not.
   185  type JumpIf struct {
   186  	Cond  Register
   187  	Label Label
   188  	Not   bool
   189  }
   190  
   191  // ProcessInstr makes the InstrProcessor process this instruction.
   192  func (j JumpIf) ProcessInstr(p InstrProcessor) {
   193  	p.ProcessJumpIfInstr(j)
   194  }
   195  
   196  func (j JumpIf) String() string {
   197  	return fmt.Sprintf("jump %s if %s is not %t", j.Label, j.Cond, j.Not)
   198  }
   199  
   200  // Call moves execution to the given continuation
   201  type Call struct {
   202  	Cont Register
   203  	Tail bool
   204  }
   205  
   206  // ProcessInstr makes the InstrProcessor process this instruction.
   207  func (c Call) ProcessInstr(p InstrProcessor) {
   208  	p.ProcessCallInstr(c)
   209  }
   210  
   211  func (c Call) String() string {
   212  	return fmt.Sprintf("call %s, tail=%t", c.Cont, c.Tail)
   213  }
   214  
   215  // MkClosure creates a new closure with the given code and upvalues and puts it in Dst.
   216  type MkClosure struct {
   217  	Dst      Register
   218  	Code     uint
   219  	Upvalues []Register
   220  }
   221  
   222  // DestReg returns the destination register of this instruction.
   223  func (m MkClosure) DestReg() Register {
   224  	return m.Dst
   225  }
   226  
   227  // WithDestReg returns the same isntruction with a new destination register.
   228  func (m MkClosure) WithDestReg(r Register) Instruction {
   229  	m.Dst = r
   230  	return m
   231  }
   232  
   233  // ProcessInstr makes the InstrProcessor process this instruction.
   234  func (m MkClosure) ProcessInstr(p InstrProcessor) {
   235  	p.ProcessMkClosureInstr(m)
   236  }
   237  
   238  func (m MkClosure) String() string {
   239  	return fmt.Sprintf("%s := mkclos(k%d; %s)", m.Dst, m.Code, joinRegisters(m.Upvalues, ", "))
   240  }
   241  
   242  func joinRegisters(regs []Register, sep string) string {
   243  	us := []string{}
   244  	for _, r := range regs {
   245  		us = append(us, r.String())
   246  	}
   247  	return strings.Join(us, sep)
   248  }
   249  
   250  // MkCont creates a new continuation for the given closure and puts it in Dst.
   251  type MkCont struct {
   252  	Dst     Register
   253  	Closure Register
   254  	Tail    bool
   255  }
   256  
   257  // DestReg returns the destination register of this instruction.
   258  func (m MkCont) DestReg() Register {
   259  	return m.Dst
   260  }
   261  
   262  // WithDestReg returns the same isntruction with a new destination register.
   263  func (m MkCont) WithDestReg(r Register) Instruction {
   264  	m.Dst = r
   265  	return m
   266  }
   267  
   268  // ProcessInstr makes the InstrProcessor process this instruction.
   269  func (m MkCont) ProcessInstr(p InstrProcessor) {
   270  	p.ProcessMkContInstr(m)
   271  }
   272  
   273  func (m MkCont) String() string {
   274  	return fmt.Sprintf("%s := mkcont(%s, tail=%t)", m.Dst, m.Closure, m.Tail)
   275  }
   276  
   277  // ClearReg resets the given register to nil (if it contained a cell, this cell
   278  // is removed).
   279  type ClearReg struct {
   280  	Dst Register
   281  }
   282  
   283  // ProcessInstr makes the InstrProcessor process this instruction.
   284  func (i ClearReg) ProcessInstr(p InstrProcessor) {
   285  	p.ProcessClearRegInstr(i)
   286  }
   287  
   288  func (i ClearReg) String() string {
   289  	return fmt.Sprintf("clrreg(%s)", i.Dst)
   290  }
   291  
   292  // MkTable creates a new empty table and puts it i Dst.
   293  type MkTable struct {
   294  	Dst Register
   295  }
   296  
   297  // ProcessInstr makes the InstrProcessor process this instruction.
   298  func (m MkTable) ProcessInstr(p InstrProcessor) {
   299  	p.ProcessMkTableInstr(m)
   300  }
   301  
   302  func (m MkTable) String() string {
   303  	return fmt.Sprintf("%s := mktable()", m.Dst)
   304  }
   305  
   306  // Lookup finds the value associated with the key Index in Table and puts it in
   307  // Dst.
   308  type Lookup struct {
   309  	Dst   Register
   310  	Table Register
   311  	Index Register
   312  }
   313  
   314  // DestReg returns the destination register of this instruction.
   315  func (s Lookup) DestReg() Register {
   316  	return s.Dst
   317  }
   318  
   319  // WithDestReg returns the same isntruction with a new destination register.
   320  func (s Lookup) WithDestReg(r Register) Instruction {
   321  	s.Dst = r
   322  	return s
   323  }
   324  
   325  // ProcessInstr makes the InstrProcessor process this instruction.
   326  func (s Lookup) ProcessInstr(p InstrProcessor) {
   327  	p.ProcessLookupInstr(s)
   328  }
   329  
   330  func (s Lookup) String() string {
   331  	return fmt.Sprintf("%s := %s[%s]", s.Dst, s.Table, s.Index)
   332  }
   333  
   334  // SetIndex associates Index with Src in the table Table.
   335  type SetIndex struct {
   336  	Table Register
   337  	Index Register
   338  	Src   Register
   339  }
   340  
   341  // ProcessInstr makes the InstrProcessor process this instruction.
   342  func (s SetIndex) ProcessInstr(p InstrProcessor) {
   343  	p.ProcessSetIndexInstr(s)
   344  }
   345  
   346  func (s SetIndex) String() string {
   347  	return fmt.Sprintf("%s[%s] := %s", s.Table, s.Index, s.Src)
   348  }
   349  
   350  // Receive will put the result of pushes in the given registers.
   351  type Receive struct {
   352  	Dst []Register
   353  }
   354  
   355  // ProcessInstr makes the InstrProcessor process this instruction.
   356  func (r Receive) ProcessInstr(p InstrProcessor) {
   357  	p.ProcessReceiveInstr(r)
   358  }
   359  
   360  func (r Receive) String() string {
   361  	return fmt.Sprintf("recv(%s)", joinRegisters(r.Dst, ", "))
   362  }
   363  
   364  // ReceiveEtc will put the result of pushes into the given registers.  Extra
   365  // pushes will be accumulated into the Etc register.
   366  type ReceiveEtc struct {
   367  	Dst []Register
   368  	Etc Register
   369  }
   370  
   371  func (r ReceiveEtc) String() string {
   372  	return fmt.Sprintf("recv(%s, ...%s)", joinRegisters(r.Dst, ", "), r.Etc)
   373  }
   374  
   375  // ProcessInstr makes the InstrProcessor process this instruction.
   376  func (r ReceiveEtc) ProcessInstr(p InstrProcessor) {
   377  	p.ProcessReceiveEtcInstr(r)
   378  }
   379  
   380  // EtcLookup finds the value at index Idx in the Etc register and puts it in
   381  // Dst.
   382  type EtcLookup struct {
   383  	Etc Register
   384  	Dst Register
   385  	Idx int
   386  }
   387  
   388  // DestReg returns the destination register of this instruction.
   389  func (l EtcLookup) DestReg() Register {
   390  	return l.Dst
   391  }
   392  
   393  // WithDestReg returns the same isntruction with a new destination register.
   394  func (l EtcLookup) WithDestReg(r Register) Instruction {
   395  	l.Dst = r
   396  	return l
   397  }
   398  
   399  func (l EtcLookup) String() string {
   400  	return fmt.Sprintf("%s := %s[%d]", l.Dst, l.Etc, l.Idx)
   401  }
   402  
   403  // ProcessInstr makes the InstrProcessor process this instruction.
   404  func (l EtcLookup) ProcessInstr(p InstrProcessor) {
   405  	p.ProcessEtcLookupInstr(l)
   406  }
   407  
   408  // FillTable fills Dst (which must contain a table) with the contents of Etc
   409  // (which must be an etc value) starting from the given index.
   410  type FillTable struct {
   411  	Etc Register
   412  	Dst Register
   413  	Idx int
   414  }
   415  
   416  func (f FillTable) String() string {
   417  	return fmt.Sprintf("fill %s with %s from %d", f.Dst, f.Etc, f.Idx)
   418  }
   419  
   420  // ProcessInstr makes the InstrProcessor process this instruction.
   421  func (f FillTable) ProcessInstr(p InstrProcessor) {
   422  	p.ProcessFillTableInstr(f)
   423  }
   424  
   425  // TruncateCloseStack truncates the close stack to Height (which should be >=
   426  // 0).  This instruction was introduced to support to-be-closed variables which
   427  // are part of Lua 5.4
   428  type TruncateCloseStack struct {
   429  	Height int
   430  }
   431  
   432  func (t TruncateCloseStack) String() string {
   433  	return fmt.Sprintf("trunc close stack to %d", t.Height)
   434  }
   435  
   436  // ProcessInstr makes the InstrProcessor process this instruction.
   437  func (t TruncateCloseStack) ProcessInstr(p InstrProcessor) {
   438  	p.ProcessTruncateCloseStackInstr(t)
   439  }
   440  
   441  // PushCloseStack truncates pushes the value Src to the close stack.  This
   442  // instruction was introduced to support to-be-closed variables which are part
   443  // of Lua 5.4
   444  type PushCloseStack struct {
   445  	Src Register
   446  }
   447  
   448  func (i PushCloseStack) String() string {
   449  	return fmt.Sprintf("push %s to close stack", i.Src)
   450  }
   451  
   452  // ProcessInstr makes the InstrProcessor process this instruction.
   453  func (i PushCloseStack) ProcessInstr(p InstrProcessor) {
   454  	p.ProcessPushCloseStackInstr(i)
   455  }
   456  
   457  // TakeRegister is not a real instruction.  It is a hint to the next stage that
   458  // the value in this register will need to be used, so not to overwrite it.  A
   459  // ReleaseRegister for the same register should be emitted some time later.
   460  type TakeRegister struct {
   461  	Reg Register
   462  }
   463  
   464  func (t TakeRegister) String() string {
   465  	return fmt.Sprintf("take %s", t.Reg)
   466  }
   467  
   468  // ProcessInstr makes the InstrProcessor process this instruction.
   469  func (t TakeRegister) ProcessInstr(p InstrProcessor) {
   470  	p.ProcessTakeRegisterInstr(t)
   471  }
   472  
   473  // ReleaseRegister is not a real instruction.  It is a hint to the next stage
   474  // that the value in this register no longer needs to be used, so can be
   475  // overwritten.  It should be preceded by a TakeRegister for the same register.
   476  type ReleaseRegister struct {
   477  	Reg Register
   478  }
   479  
   480  func (r ReleaseRegister) String() string {
   481  	return fmt.Sprintf("release %s", r.Reg)
   482  }
   483  
   484  // ProcessInstr makes the InstrProcessor process this instruction.
   485  func (r ReleaseRegister) ProcessInstr(p InstrProcessor) {
   486  	p.ProcessReleaseRegisterInstr(r)
   487  }
   488  
   489  // DeclareLabel is not a real IR instruction.  It is a placeholder for the
   490  // destination location of a jump label.  Being an instruction makes it easier
   491  // to perform IR code transformations and keep the labels in correct locations.
   492  type DeclareLabel struct {
   493  	Label Label
   494  }
   495  
   496  func (l DeclareLabel) String() string {
   497  	return fmt.Sprintf("L%d:", l.Label)
   498  }
   499  
   500  // ProcessInstr makes the InstrProcessor process this instruction.
   501  func (l DeclareLabel) ProcessInstr(p InstrProcessor) {
   502  	p.ProcessDeclareLabelInstr(l)
   503  }
   504  
   505  // PrepForLoop prepares a for loop
   506  type PrepForLoop struct {
   507  	Start, Stop, Step Register
   508  }
   509  
   510  func (i PrepForLoop) String() string {
   511  	return fmt.Sprintf("prepfor %s, %s, %s", i.Start, i.Stop, i.Step)
   512  }
   513  
   514  func (i PrepForLoop) ProcessInstr(p InstrProcessor) {
   515  	p.ProcessPrepForLoopInstr(i)
   516  }
   517  
   518  // AdvForLoop advances a for loop
   519  
   520  type AdvForLoop struct {
   521  	Start, Stop, Step Register
   522  }
   523  
   524  func (i AdvForLoop) String() string {
   525  	return fmt.Sprintf("advfor %s, %s, %s", i.Start, i.Stop, i.Step)
   526  }
   527  
   528  func (i AdvForLoop) ProcessInstr(p InstrProcessor) {
   529  	p.ProcessAdvForLoopInstr(i)
   530  }