github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/compiler/compiler_drop.go (about) 1 package compiler 2 3 import ( 4 "github.com/bananabytelabs/wazero/internal/asm" 5 "github.com/bananabytelabs/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, 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 }