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  }