wa-lang.org/wazero@v1.0.2/internal/engine/compiler/compiler_drop.go (about)

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