gitee.com/quant1x/num@v0.3.2/asm/c2goasm/subroutine.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2017 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "fmt" 21 "regexp" 22 "strconv" 23 "strings" 24 "unicode" 25 ) 26 27 var regexpRet = regexp.MustCompile(`^\s*ret`) 28 29 type Subroutine struct { 30 name string 31 body []string 32 epilogue Epilogue 33 table Table 34 } 35 36 type Global struct { 37 dotGlobalLine int 38 globalName string 39 globalLabelLine int 40 } 41 42 var globlRe = regexp.MustCompile(`^\s*.globl\s+([^\s]+)\s*.*`) 43 44 func splitOnGlobals(lines []string) []Global { 45 46 var result []Global 47 48 for index, line := range lines { 49 if globlRe.MatchString(line) { 50 match := globlRe.FindStringSubmatch(line) 51 scrambled := match[1] 52 name := extractName(scrambled) 53 54 labelLine := findLabel(lines, scrambled) 55 56 result = append(result, Global{dotGlobalLine: index, globalName: name, globalLabelLine: labelLine}) 57 } 58 } 59 60 return result 61 } 62 63 // Segment the source into multiple routines 64 func segmentSource(src []string) []Subroutine { 65 66 globals := splitOnGlobals(src) 67 68 if len(globals) == 0 { 69 return []Subroutine{} 70 } 71 72 subroutines := []Subroutine{} 73 74 splitBegin := globals[0].dotGlobalLine 75 for iglobal, global := range globals { 76 splitEnd := len(src) 77 if iglobal < len(globals)-1 { 78 splitEnd = globals[iglobal+1].dotGlobalLine 79 } 80 81 // Search for `ret` statement 82 for lineRet := splitBegin; lineRet < splitEnd; lineRet++ { 83 if match := regexpRet.FindStringSubmatch(src[lineRet]); len(match) > 0 { 84 85 newsub := extractSubroutine(lineRet, src, global) 86 87 subroutines = append(subroutines, newsub) 88 89 break 90 } 91 } 92 93 splitBegin = splitEnd 94 } 95 96 return subroutines 97 } 98 99 var disabledForTesting = false 100 101 func extractSubroutine(lineRet int, src []string, global Global) Subroutine { 102 103 bodyStart := global.globalLabelLine + 1 104 bodyEnd := lineRet + 1 105 106 // loop until all missing labels are found 107 for !disabledForTesting { 108 missingLabels := getMissingLabels(src[bodyStart:bodyEnd]) 109 110 if len(missingLabels) == 0 { 111 break 112 } 113 114 // add the missing lines in order to find the missing labels 115 postEpilogueLines := getMissingLines(src, bodyEnd-1, missingLabels) 116 117 bodyEnd += postEpilogueLines 118 } 119 120 subroutine := Subroutine{ 121 name: global.globalName, 122 body: src[bodyStart:bodyEnd], 123 epilogue: extractEpilogue(src[bodyStart:bodyEnd]), 124 } 125 126 // Remove prologue lines from subroutine 127 subroutine.removePrologueLines(src, bodyStart, bodyEnd) 128 129 return subroutine 130 } 131 132 func (s *Subroutine) removePrologueLines(src []string, bodyStart int, bodyEnd int) { 133 134 prologueLines := getPrologueLines(src[bodyStart:bodyEnd], &s.epilogue) 135 136 // Remove prologue lines from body 137 s.body = s.body[prologueLines:] 138 139 // Adjust range of epilogue accordingly 140 s.epilogue.Start -= prologueLines 141 s.epilogue.End -= prologueLines 142 } 143 144 func extractEpilogue(src []string) Epilogue { 145 146 for iline, line := range src { 147 148 if match := regexpRet.FindStringSubmatch(line); len(match) > 0 { 149 150 // Found closing ret statement, start searching back to first non epilogue instruction 151 epilogueStart := iline 152 for ; epilogueStart >= 0; epilogueStart-- { 153 if !isEpilogueInstruction(src[epilogueStart]) { 154 epilogueStart++ 155 break 156 } 157 } 158 159 epilogue := extractEpilogueInfo(src, epilogueStart, iline+1) 160 161 return epilogue 162 } 163 } 164 165 panic("Failed to find 'ret' instruction") 166 } 167 168 func getMissingLabels(src []string) map[string]bool { 169 170 labelMap := make(map[string]bool) 171 jumpMap := make(map[string]bool) 172 173 for _, line := range src { 174 175 line, _ := stripComments(line) 176 if _, label := fixLabels(line); label != "" { 177 labelMap[label] = true 178 } 179 if _, _, label := upperCaseJumps(line); label != "" { 180 jumpMap[label] = true 181 } 182 183 } 184 185 for label, _ := range labelMap { 186 if _, ok := jumpMap[label]; ok { 187 delete(jumpMap, label) 188 } 189 } 190 191 return jumpMap 192 } 193 194 func getMissingLines(src []string, lineRet int, missingLabels map[string]bool) int { 195 196 var iline int 197 // first scan until we've found the missing labels 198 for iline = lineRet; len(missingLabels) > 0 && iline < len(src); iline++ { 199 line, _ := stripComments(src[iline]) 200 _, label := fixLabels(line) 201 if label != "" { 202 if _, ok := missingLabels[label]; ok { 203 delete(missingLabels, label) 204 } 205 } 206 } 207 // then scan until we find an (unconditional) JMP 208 for ; iline < len(src); iline++ { 209 line, _ := stripComments(src[iline]) 210 _, jump, _ := upperCaseJumps(line) 211 if jump == "JMP" { 212 break 213 } 214 } 215 216 return iline - lineRet 217 } 218 219 func getPrologueLines(lines []string, epilogue *Epilogue) int { 220 221 index, line := 0, "" 222 223 for index, line = range lines { 224 225 var skip bool 226 line, skip = stripComments(line) // Remove ## comments 227 if skip { 228 continue 229 } 230 231 if !epilogue.isPrologueInstruction(line) { 232 break 233 } 234 } 235 236 return index 237 } 238 239 func findLabel(lines []string, label string) int { 240 241 labelDef := label + ":" 242 243 for index, line := range lines { 244 if strings.HasPrefix(line, labelDef) { 245 return index 246 } 247 } 248 249 panic(fmt.Sprintf("Failed to find label: %s", labelDef)) 250 } 251 252 func extractNamePart(part string) (int, string) { 253 254 digits := 0 255 for _, d := range part { 256 if unicode.IsDigit(d) { 257 digits += 1 258 } else { 259 break 260 } 261 } 262 length, _ := strconv.Atoi(part[:digits]) 263 return digits + length, part[digits:(digits + length)] 264 } 265 266 func extractName(name string) string { 267 268 // Only proceed for C++ mangled names 269 if !(strings.HasPrefix(name, "_ZN") || strings.HasPrefix(name, "__Z")) { 270 return name 271 } 272 273 var parts []string 274 275 // Parse C++ mangled name in the form of '_ZN4Simd4Avx213Yuv444pToBgraEPKhmS2_mS2_mmmPhmh' 276 for index, ch := range name { 277 if unicode.IsDigit(ch) { 278 279 for index < len(name) { 280 size, part := extractNamePart(name[index:]) 281 if size == 0 { 282 break 283 } 284 285 parts = append(parts, part) 286 index += size 287 } 288 289 break 290 } 291 } 292 293 return strings.Join(parts, "") 294 }