gitee.com/quant1x/num@v0.3.2/asm/c2goasm/epilogue.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  )
    24  
    25  type Epilogue struct {
    26  	Pops             []string // list of registers that are popped of the stack
    27  	SetRbpInstr      bool     // is there an instruction to set Rbp in epilogue?
    28  	StackSize        uint     // the size of the C stack
    29  	AlignedStack     bool     // is this an aligned stack?
    30  	AlignValue       uint     // alignment value in case of an aligned stack
    31  	VZeroUpper       bool     // is there a vzeroupper instruction in the epilogue?
    32  	Start, End       int      // start and ending lines of epilogue
    33  	_missingPops     int      // internal variable to identify first push without a corresponding pop
    34  	_stackGrowthSign int      // direction to grow stack in case of detecting a push without a corresponding pop
    35  }
    36  
    37  var regexpAddRsp = regexp.MustCompile(`^\s*add\s*rsp, ([0-9]+)$`)
    38  var regexpAndRsp = regexp.MustCompile(`^\s*and\s*rsp, \-([0-9]+)$`)
    39  var regexpSubRsp = regexp.MustCompile(`^\s*sub\s*rsp, ([0-9]+)$`)
    40  var regexpLeaRsp = regexp.MustCompile(`^\s*lea\s*rsp, `)
    41  var regexpPop = regexp.MustCompile(`^\s*pop\s*([a-z0-9]+)$`)
    42  var regexpPush = regexp.MustCompile(`^\s*push\s*([a-z0-9]+)$`)
    43  var regexpMov = regexp.MustCompile(`^\s*mov\s*([a-z0-9]+), ([a-z0-9]+)$`)
    44  var regexpVZeroUpper = regexp.MustCompile(`^\s*vzeroupper\s*$`)
    45  var regexpReturn = regexp.MustCompile(`^\s*ret\s*$`)
    46  
    47  type Stack struct {
    48  	alignedStack bool // is this an aligned stack?
    49  
    50  	goSavedSP      uint // space to save a copy of the original Stack Pointer as passed in by Go (for aligned stacks)
    51  	goArgCopies    uint // space used to store copies of golang args not passed in registers (arguments 7 and higher)
    52  	localSpace     uint // stack space used by C code
    53  	freeSpace      uint // free stack space used for CALLs
    54  	untouchedSpace uint // untouched space to prevent overwriting return address for final RET statement
    55  }
    56  
    57  func NewStack(epilogue Epilogue, arguments int, stackSpaceForCalls uint) Stack {
    58  
    59  	s := Stack{localSpace: epilogue.StackSize, alignedStack: epilogue.AlignedStack, freeSpace: stackSpaceForCalls}
    60  
    61  	if arguments-len(registers) > 0 {
    62  		s.goArgCopies = uint(8 * (arguments - len(registers)))
    63  	}
    64  
    65  	if s.alignedStack {
    66  		// For an aligned stack we need to save the original Stack Pointer as passed in by Go
    67  		s.goSavedSP = originalStackPointer
    68  
    69  		// We are rounding freeSpace to a multiple of the  alignment value
    70  		s.freeSpace = (s.freeSpace + epilogue.AlignValue - 1) & ^(epilogue.AlignValue - 1)
    71  
    72  		// Create unused space at the bottom of the stack to guarantee alignment
    73  		s.untouchedSpace = epilogue.AlignValue
    74  	} else {
    75  		// Only when we are using no stack whatsoever, do we not need to reserve space to save the return address
    76  		if s.freeSpace+s.localSpace+s.goArgCopies+s.goSavedSP > 0 {
    77  			s.untouchedSpace = 8
    78  		}
    79  	}
    80  
    81  	return s
    82  }
    83  
    84  // Get total local stack frame size (for Go) used in TEXT definition
    85  func (s Stack) GolangLocalStackFrameSize() uint {
    86  	return s.untouchedSpace + s.freeSpace + s.localSpace + s.goArgCopies + s.goSavedSP
    87  }
    88  
    89  // Get offset to adjust Stack Pointer appropriately for C code
    90  func (s Stack) StackPointerOffsetForC() uint {
    91  	return s.untouchedSpace + s.freeSpace
    92  }
    93  
    94  // Get offset (from C Stack Pointer) for saving original Golang Stack Pointer
    95  func (s Stack) OffsetForSavedSP() uint {
    96  	if s.goSavedSP == 0 {
    97  		panic("There should be space reserved for OffsetForSavedSP")
    98  	}
    99  	return s.localSpace + s.goArgCopies
   100  }
   101  
   102  // Get offset (from C Stack Pointer) for copy of Golang arguments 7 and higher
   103  func (s Stack) OffsetForGoArg(iarg int) uint {
   104  
   105  	offset := uint((iarg - len(registers)) * 8)
   106  	if offset > s.goArgCopies {
   107  		panic("Offset for higher number argument asked for than reserved")
   108  	}
   109  	return s.localSpace + offset
   110  }
   111  
   112  func extractEpilogueInfo(src []string, sliceStart, sliceEnd int) Epilogue {
   113  
   114  	epilogue := Epilogue{Start: sliceStart, End: sliceEnd}
   115  
   116  	// Iterate over epilogue, starting from last instruction
   117  	for ipost := sliceEnd - 1; ipost >= sliceStart; ipost-- {
   118  		line := src[ipost]
   119  
   120  		if !epilogue.extractEpilogue(line) {
   121  			panic(fmt.Sprintf("Unknown line for epilogue: %s", line))
   122  		}
   123  	}
   124  
   125  	return epilogue
   126  }
   127  
   128  func (e *Epilogue) extractEpilogue(line string) bool {
   129  
   130  	if match := regexpPop.FindStringSubmatch(line); len(match) > 1 {
   131  		register := match[1]
   132  
   133  		e.Pops = append(e.Pops, register)
   134  		if register == "rbp" {
   135  			e.SetRbpInstr = true
   136  		}
   137  	} else if match := regexpAddRsp.FindStringSubmatch(line); len(match) > 1 {
   138  		size, _ := strconv.Atoi(match[1])
   139  		e.StackSize = uint(size)
   140  	} else if match := regexpLeaRsp.FindStringSubmatch(line); len(match) > 0 {
   141  		e.AlignedStack = true
   142  	} else if match := regexpVZeroUpper.FindStringSubmatch(line); len(match) > 0 {
   143  		e.VZeroUpper = true
   144  	} else if match := regexpMov.FindStringSubmatch(line); len(match) > 2 && match[1] == "rsp" && match[2] == "rbp" {
   145  		// no action to take
   146  	} else if match := regexpReturn.FindStringSubmatch(line); len(match) > 0 {
   147  		// no action to take
   148  	} else {
   149  		return false
   150  	}
   151  
   152  	return true
   153  }
   154  
   155  func isEpilogueInstruction(line string) bool {
   156  
   157  	return (&Epilogue{}).extractEpilogue(line)
   158  }
   159  
   160  func (e *Epilogue) isPrologueInstruction(line string) bool {
   161  
   162  	if match := regexpPush.FindStringSubmatch(line); len(match) > 1 {
   163  		hasCorrespondingPop := listContains(match[1], e.Pops)
   164  		if !hasCorrespondingPop {
   165  			e._missingPops++
   166  			if e._missingPops == 1 { // only for first missing pop, set initial direction of growth to adapt check
   167  				if e.StackSize > 0 {
   168  					// Missing corresponding `pop` but rsp was modified directly in epilogue (see test-case pro/epilogue6)
   169  					e._stackGrowthSign = -1
   170  				} else {
   171  					// Missing corresponding `pop` meaning rsp is grown indirectly in prologue (see test-case pro/epilogue7)
   172  					e._stackGrowthSign = 1
   173  				}
   174  			}
   175  			e.StackSize += uint(8 * e._stackGrowthSign)
   176  			if e.StackSize == 0 && e._stackGrowthSign == -1 {
   177  				e._stackGrowthSign = 1 // flip direction once stack has shrunk to zero
   178  			}
   179  		}
   180  		return true
   181  	} else if match := regexpMov.FindStringSubmatch(line); len(match) > 2 && match[1] == "rbp" && match[2] == "rsp" {
   182  		if e.SetRbpInstr {
   183  			return true
   184  		} else {
   185  			panic(fmt.Sprintf("mov found but not expected to be set: %s", line))
   186  		}
   187  	} else if match := regexpAndRsp.FindStringSubmatch(line); len(match) > 1 {
   188  		align, _ := strconv.Atoi(match[1])
   189  		if e.AlignedStack && align == 8 {
   190  			// golang stack is already 8 byte aligned so we can effectively disable the aligned stack
   191  			e.AlignedStack = false
   192  		} else {
   193  			e.AlignValue = uint(align)
   194  		}
   195  
   196  		return true
   197  	} else if match := regexpSubRsp.FindStringSubmatch(line); len(match) > 1 {
   198  		space, _ := strconv.Atoi(match[1])
   199  		if !e.AlignedStack && e.StackSize == uint(space) {
   200  			return true
   201  		} else if e.StackSize == 0 || e.StackSize == uint(space) {
   202  			e.StackSize = uint(space) // Update stack size when found in header (and missing in footer due to `lea` instruction)
   203  			return true
   204  		} else {
   205  			panic(fmt.Sprintf("'sub rsp' found but in unexpected scenario: %s", line))
   206  		}
   207  	}
   208  
   209  	return false
   210  }
   211  
   212  func listContains(value string, list []string) bool {
   213  	for _, v := range list {
   214  		if v == value {
   215  			return true
   216  		}
   217  	}
   218  	return false
   219  }