github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/wazevoapi/debug_options.go (about) 1 package wazevoapi 2 3 import ( 4 "context" 5 "encoding/hex" 6 "fmt" 7 "math/rand" 8 "os" 9 "time" 10 ) 11 12 // These consts are used various places in the wazevo implementations. 13 // Instead of defining them in each file, we define them here so that we can quickly iterate on 14 // debugging without spending "where do we have debug logging?" time. 15 16 // ----- Debug logging ----- 17 // These consts must be disabled by default. Enable them only when debugging. 18 19 const ( 20 FrontEndLoggingEnabled = false 21 SSALoggingEnabled = false 22 RegAllocLoggingEnabled = false 23 ) 24 25 // ----- Output prints ----- 26 // These consts must be disabled by default. Enable them only when debugging. 27 28 const ( 29 PrintSSA = false 30 PrintOptimizedSSA = false 31 PrintBlockLaidOutSSA = false 32 PrintSSAToBackendIRLowering = false 33 PrintRegisterAllocated = false 34 PrintFinalizedMachineCode = false 35 PrintMachineCodeHexPerFunction = printMachineCodeHexPerFunctionUnmodified || PrintMachineCodeHexPerFunctionDisassemblable //nolint 36 printMachineCodeHexPerFunctionUnmodified = false 37 // PrintMachineCodeHexPerFunctionDisassemblable prints the machine code while modifying the actual result 38 // to make it disassemblable. This is useful when debugging the final machine code. See the places where this is used for detail. 39 // When this is enabled, functions must not be called. 40 PrintMachineCodeHexPerFunctionDisassemblable = false 41 ) 42 43 // ----- Validations ----- 44 const ( 45 // SSAValidationEnabled enables the SSA validation. This is disabled by default since the operation is expensive. 46 SSAValidationEnabled = false 47 ) 48 49 // ----- Stack Guard Check ----- 50 const ( 51 // StackGuardCheckEnabled enables the stack guard check to ensure that our stack bounds check works correctly. 52 StackGuardCheckEnabled = false 53 StackGuardCheckGuardPageSize = 8096 54 ) 55 56 // CheckStackGuardPage checks the given stack guard page is not corrupted. 57 func CheckStackGuardPage(s []byte) { 58 for i := 0; i < StackGuardCheckGuardPageSize; i++ { 59 if s[i] != 0 { 60 panic( 61 fmt.Sprintf("BUG: stack guard page is corrupted:\n\tguard_page=%s\n\tstack=%s", 62 hex.EncodeToString(s[:StackGuardCheckGuardPageSize]), 63 hex.EncodeToString(s[StackGuardCheckGuardPageSize:]), 64 )) 65 } 66 } 67 } 68 69 // ----- Deterministic compilation verifier ----- 70 71 const ( 72 // DeterministicCompilationVerifierEnabled enables the deterministic compilation verifier. This is disabled by default 73 // since the operation is expensive. But when in doubt, enable this to make sure the compilation is deterministic. 74 DeterministicCompilationVerifierEnabled = false 75 DeterministicCompilationVerifyingIter = 5 76 ) 77 78 type ( 79 verifierState struct { 80 initialCompilationDone bool 81 maybeRandomizedIndexes []int 82 r *rand.Rand 83 values map[string]string 84 } 85 verifierStateContextKey struct{} 86 currentFunctionNameKey struct{} 87 ) 88 89 // NewDeterministicCompilationVerifierContext creates a new context with the deterministic compilation verifier used per wasm.Module. 90 func NewDeterministicCompilationVerifierContext(ctx context.Context, localFunctions int) context.Context { 91 maybeRandomizedIndexes := make([]int, localFunctions) 92 for i := range maybeRandomizedIndexes { 93 maybeRandomizedIndexes[i] = i 94 } 95 r := rand.New(rand.NewSource(time.Now().UnixNano())) 96 return context.WithValue(ctx, verifierStateContextKey{}, &verifierState{ 97 r: r, maybeRandomizedIndexes: maybeRandomizedIndexes, values: map[string]string{}, 98 }) 99 } 100 101 // DeterministicCompilationVerifierRandomizeIndexes randomizes the indexes for the deterministic compilation verifier. 102 // To get the randomized index, use DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex. 103 func DeterministicCompilationVerifierRandomizeIndexes(ctx context.Context) { 104 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 105 if !state.initialCompilationDone { 106 // If this is the first attempt, we use the index as-is order. 107 state.initialCompilationDone = true 108 return 109 } 110 r := state.r 111 r.Shuffle(len(state.maybeRandomizedIndexes), func(i, j int) { 112 state.maybeRandomizedIndexes[i], state.maybeRandomizedIndexes[j] = state.maybeRandomizedIndexes[j], state.maybeRandomizedIndexes[i] 113 }) 114 } 115 116 // DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex returns the randomized index for the given `index` 117 // which is assigned by DeterministicCompilationVerifierRandomizeIndexes. 118 func DeterministicCompilationVerifierGetRandomizedLocalFunctionIndex(ctx context.Context, index int) int { 119 state := ctx.Value(verifierStateContextKey{}).(*verifierState) 120 ret := state.maybeRandomizedIndexes[index] 121 return ret 122 } 123 124 // VerifyOrSetDeterministicCompilationContextValue verifies that the `newValue` is the same as the previous value for the given `scope` 125 // and the current function name. If the previous value doesn't exist, it sets the value to the given `newValue`. 126 // 127 // If the verification fails, this prints the diff and exits the process. 128 func VerifyOrSetDeterministicCompilationContextValue(ctx context.Context, scope string, newValue string) { 129 fn := ctx.Value(currentFunctionNameKey{}).(string) 130 key := fn + ": " + scope 131 verifierCtx := ctx.Value(verifierStateContextKey{}).(*verifierState) 132 oldValue, ok := verifierCtx.values[key] 133 if !ok { 134 verifierCtx.values[key] = newValue 135 return 136 } 137 if oldValue != newValue { 138 fmt.Printf( 139 `BUG: Deterministic compilation failed for function%s at scope="%s". 140 141 This is mostly due to (but might not be limited to): 142 * Resetting ssa.Builder, backend.Compiler or frontend.Compiler, etc doens't work as expected, and the compilation has been affected by the previous iterations. 143 * Using a map with non-deterministic iteration order. 144 145 ---------- [old] ---------- 146 %s 147 148 ---------- [new] ---------- 149 %s 150 `, 151 fn, scope, oldValue, newValue, 152 ) 153 os.Exit(1) 154 } 155 } 156 157 // nolint 158 const NeedFunctionNameInContext = PrintSSA || 159 PrintOptimizedSSA || 160 PrintBlockLaidOutSSA || 161 PrintSSAToBackendIRLowering || 162 PrintRegisterAllocated || 163 PrintFinalizedMachineCode || 164 PrintMachineCodeHexPerFunction || 165 DeterministicCompilationVerifierEnabled 166 167 // SetCurrentFunctionName sets the current function name to the given `functionName`. 168 func SetCurrentFunctionName(ctx context.Context, functionName string) context.Context { 169 return context.WithValue(ctx, currentFunctionNameKey{}, functionName) 170 } 171 172 // GetCurrentFunctionName returns the current function name. 173 func GetCurrentFunctionName(ctx context.Context) string { 174 return ctx.Value(currentFunctionNameKey{}).(string) 175 } 176 177 // ----- High Register Pressure ----- 178 179 type highRegisterPressureContextKey struct{} 180 181 // EnableHighRegisterPressure enables the high register pressure mode. 182 func EnableHighRegisterPressure(ctx context.Context) context.Context { 183 ctx = context.WithValue(ctx, highRegisterPressureContextKey{}, true) 184 return ctx 185 } 186 187 // IsHighRegisterPressure returns true if the current compilation is under high register pressure. 188 func IsHighRegisterPressure(ctx context.Context) bool { 189 return ctx.Value(highRegisterPressureContextKey{}) != nil 190 }