github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/compiler/compiler_drop.go (about)

     1  package compiler
     2  
     3  import (
     4  	"github.com/wasilibs/wazerox/internal/asm"
     5  	"github.com/wasilibs/wazerox/internal/wazeroir"
     6  )
     7  
     8  // compileDropRange adds instruction to drop the values on the target range
     9  // in an architecture independent way.
    10  func compileDropRange(c compiler, raw uint64) (err error) {
    11  	r := wazeroir.InclusiveRangeFromU64(raw)
    12  	locationStack := c.runtimeValueLocationStack()
    13  	if r.Start < 0 {
    14  		return
    15  	} else if r.Start == 0 {
    16  		for i := 0; i <= int(r.End); i++ {
    17  			if loc := locationStack.pop(); loc.onRegister() {
    18  				locationStack.releaseRegister(loc)
    19  			}
    20  		}
    21  		return
    22  	}
    23  
    24  	// If the top value is alive, we must ensure that it is not located as a conditional.
    25  	// Otherwise, the conditional flag might end up modified by the following operation.
    26  	if err = c.maybeCompileMoveTopConditionalToGeneralPurposeRegister(); err != nil {
    27  		return
    28  	}
    29  
    30  	dropValues, liveValues := locationStack.dropsLivesForInclusiveRange(r)
    31  
    32  	// Frees all the registers used by drop target values.
    33  	for i := range dropValues {
    34  		dv := &dropValues[i]
    35  		if dv.onRegister() {
    36  			locationStack.releaseRegister(dv)
    37  		}
    38  	}
    39  
    40  	// These registers are not nil if a live value of that type is currently located on the memory stack.
    41  	// In order to migrate these values, we can use them below.
    42  	gpTmp, vecTmp, err := getTemporariesForStackedLiveValues(c, liveValues)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	// Reset the stack pointer below the end.
    48  	locationStack.sp -= uint64(len(liveValues) + len(dropValues))
    49  
    50  	// Push back the live values again.
    51  	for i := range liveValues {
    52  		live := &liveValues[i]
    53  		migrateLiveValue(c, live, gpTmp, vecTmp)
    54  	}
    55  	return
    56  }
    57  
    58  // migrateLiveValue migrates the live value `live` into the top of the stack. It might be located on the stack
    59  // and in that case, we have to load it into either `generalPurposeTmpReg` or `vectorTmpReg` temporarily, and
    60  // write it back into the *new* stack location.
    61  func migrateLiveValue(c compiler, live *runtimeValueLocation, generalPurposeTmpReg, vectorTmpReg asm.Register) {
    62  	if live.valueType == runtimeValueTypeV128Hi {
    63  		// Higher bits of vector was already handled together with the lower bits.
    64  		return
    65  	}
    66  
    67  	previouslyOnStack := live.onStack()
    68  	if previouslyOnStack {
    69  		// If the value is on the stack, load the value on the old location into the temporary value,
    70  		// and then write it back to the new memory location below.
    71  		switch live.getRegisterType() {
    72  		case registerTypeGeneralPurpose:
    73  			live.setRegister(generalPurposeTmpReg)
    74  		case registerTypeVector:
    75  			live.setRegister(vectorTmpReg)
    76  		}
    77  		// Load the value into tmp.
    78  		c.compileLoadValueOnStackToRegister(live)
    79  	}
    80  
    81  	var newLocation *runtimeValueLocation
    82  	if live.valueType == runtimeValueTypeV128Lo {
    83  		newLocation = c.pushVectorRuntimeValueLocationOnRegister(live.register)
    84  	} else {
    85  		newLocation = c.pushRuntimeValueLocationOnRegister(live.register, live.valueType)
    86  	}
    87  
    88  	if previouslyOnStack {
    89  		// This case, the location is on the temporary register. Therefore,
    90  		// we have to release the value there into the *new* memory location
    91  		// so that the tmp can be used for subsequent live value migrations.
    92  		c.compileReleaseRegisterToStack(newLocation)
    93  	}
    94  }
    95  
    96  func getTemporariesForStackedLiveValues(c compiler, liveValues []runtimeValueLocation) (gpTmp, vecTmp asm.Register, err error) {
    97  	gpTmp, vecTmp = asm.NilRegister, asm.NilRegister
    98  	for i := range liveValues {
    99  		l := &liveValues[i]
   100  		if l.onStack() {
   101  			if rt := l.getRegisterType(); rt == registerTypeGeneralPurpose && gpTmp == asm.NilRegister {
   102  				gpTmp, err = c.allocateRegister(registerTypeGeneralPurpose)
   103  				if err != nil {
   104  					return
   105  				}
   106  			} else if rt == registerTypeVector && vecTmp == asm.NilRegister {
   107  				vecTmp, err = c.allocateRegister(registerTypeVector)
   108  				if err != nil {
   109  					return
   110  				}
   111  			}
   112  		}
   113  	}
   114  	return
   115  }
   116  
   117  // dropsLivesForInclusiveRange returns the live and drop target values for the given wazeroir.InclusiveRange.
   118  func (v *runtimeValueLocationStack) dropsLivesForInclusiveRange(
   119  	r wazeroir.InclusiveRange,
   120  ) (dropValues, liveValues []runtimeValueLocation) {
   121  	// liveValues are must be pushed backed after dropping the target range.
   122  	liveValues = v.stack[v.sp-uint64(r.Start) : v.sp]
   123  	// dropValues are the values on the drop target range.
   124  	dropValues = v.stack[v.sp-uint64(r.End)-1 : v.sp-uint64(r.Start)]
   125  	return
   126  }