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

     1  package ir
     2  
     3  import (
     4  	"github.com/arnodel/golua/ops"
     5  )
     6  
     7  // FoldConstants uses the given FoldFunc to fold the code items in the given
     8  // constant slice.
     9  func FoldConstants(consts []Constant, f FoldFunc) []Constant {
    10  	fConsts := make([]Constant, len(consts))
    11  	for i, k := range consts {
    12  		if c, ok := k.(*Code); ok {
    13  			fc := FoldCode(*c, f)
    14  			fConsts[i] = &fc
    15  		} else {
    16  			fConsts[i] = k
    17  		}
    18  	}
    19  	return fConsts
    20  }
    21  
    22  // FoldCode uses the given FoldFunc to fold the instructions in the given code.
    23  // Returns a new Code instande with folded code.
    24  func FoldCode(c Code, f FoldFunc) Code {
    25  	if f == nil {
    26  		return c
    27  	}
    28  	var s foldStack
    29  	var i1 Instruction
    30  	var l1 int
    31  	for i, i2 := range c.Instructions {
    32  		l2 := c.Lines[i]
    33  		if i1 != nil {
    34  			i1, i2 = f(i1, i2, c.Registers)
    35  			switch {
    36  			case i1 == nil && i2 == nil:
    37  				// Folded to nothing, pop from the stack to be able to fold the
    38  				// next instruction.
    39  				l2, i2 = s.pop()
    40  			case i1 == nil:
    41  				// Folded to i2
    42  				l2 = mergeLines(l1, l2)
    43  			case i2 == nil:
    44  				// Folded to i1
    45  				i1, i2 = nil, i1
    46  				l2 = mergeLines(l1, l2)
    47  			default:
    48  				// Not folded
    49  			}
    50  		}
    51  		if i1 != nil {
    52  			s.push(l1, i1)
    53  		}
    54  		i1 = i2
    55  		l1 = l2
    56  	}
    57  	if i1 != nil {
    58  		s.push(l1, i1)
    59  	}
    60  	c.Lines = s.lines
    61  	c.Instructions = s.instructions
    62  	return c
    63  }
    64  
    65  // A FoldFunc can turn 2 instructions into fewer instructions (in which case
    66  // some of the returned instructions are nil).
    67  type FoldFunc func(i1, i2 Instruction, regs []RegData) (Instruction, Instruction)
    68  
    69  // DefaultFold applies a few simple folds
    70  var DefaultFold FoldFunc = nil
    71  
    72  // FoldTakeRelease folds the following
    73  //
    74  // (take r1; release r1) ==> ()
    75  func FoldTakeRelease(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) {
    76  	t1, ok := i1.(TakeRegister)
    77  	if !ok {
    78  		return i1, i2
    79  	}
    80  	r2, ok := i2.(ReleaseRegister)
    81  	if !ok {
    82  		return i1, i2
    83  	}
    84  	if t1.Reg != r2.Reg {
    85  		return i1, i2
    86  	}
    87  	return nil, nil
    88  }
    89  
    90  // FoldMoveReg folds the following
    91  //
    92  // (r1 <- X; r2 <- r1) ==> r2 <- X
    93  func FoldMoveReg(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) {
    94  	sr1, ok := i1.(SetRegInstruction)
    95  	if !ok {
    96  		return i1, i2
    97  	}
    98  	dst := sr1.DestReg()
    99  	if regs[dst].IsCell {
   100  		return i1, i2
   101  	}
   102  	tr2, ok := i2.(Transform)
   103  	if !ok {
   104  		return i1, i2
   105  	}
   106  	if tr2.Op != ops.OpId || dst != tr2.Src {
   107  		return i1, i2
   108  	}
   109  	return nil, sr1.WithDestReg(tr2.Dst)
   110  }
   111  
   112  // ComposeFolds takes several fold functions are composes them into one single
   113  // function applying all the folds.
   114  func ComposeFolds(fs ...FoldFunc) FoldFunc {
   115  	return func(i1, i2 Instruction, regs []RegData) (Instruction, Instruction) {
   116  		for _, f := range fs {
   117  			i1, i2 = f(i1, i2, regs)
   118  			if i1 == nil || i2 == nil {
   119  				break
   120  			}
   121  		}
   122  		return i1, i2
   123  	}
   124  }
   125  
   126  type foldStack struct {
   127  	lines        []int
   128  	instructions []Instruction
   129  }
   130  
   131  func (s *foldStack) push(l int, i Instruction) {
   132  	s.lines = append(s.lines, l)
   133  	s.instructions = append(s.instructions, i)
   134  }
   135  
   136  func (s *foldStack) empty() bool {
   137  	return len(s.instructions) == 0
   138  }
   139  
   140  func (s *foldStack) pop() (l int, i Instruction) {
   141  	last := len(s.instructions) - 1
   142  	if last < 0 {
   143  		return
   144  	}
   145  	l = s.lines[last]
   146  	i = s.instructions[last]
   147  	s.lines = s.lines[:last]
   148  	s.instructions = s.instructions[:last]
   149  	return
   150  }
   151  
   152  func mergeLines(l1, l2 int) int {
   153  	if l1 != 0 {
   154  		return l1
   155  	}
   156  	return l2
   157  }