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 }