github.com/cloudwego/frugal@v0.1.15/internal/atm/hir/call.go (about)

     1  /*
     2   * Copyright 2022 ByteDance 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 hir
    18  
    19  import (
    20      `fmt`
    21      `runtime`
    22      `sync`
    23      `unsafe`
    24  
    25      `github.com/cloudwego/frugal/internal/atm/abi`
    26      `github.com/cloudwego/frugal/internal/rt`
    27  )
    28  
    29  type (
    30      CallType uint8
    31  )
    32  
    33  const (
    34      CCall CallType = iota
    35      GCall
    36      ICall
    37  )
    38  
    39  type CallState interface {
    40      Gr(id GenericRegister) uint64
    41      Pr(id PointerRegister) unsafe.Pointer
    42      SetGr(id GenericRegister, val uint64)
    43      SetPr(id PointerRegister, val unsafe.Pointer)
    44  }
    45  
    46  type CallHandle struct {
    47      Id    int
    48      Slot  int
    49      Type  CallType
    50      Func  unsafe.Pointer
    51      proxy func(CallContext)
    52  }
    53  
    54  func (self *CallHandle) Name() string {
    55      return runtime.FuncForPC(uintptr(self.Func)).Name()
    56  }
    57  
    58  func (self *CallHandle) Call(r CallState, p *Ir) {
    59      self.proxy(CallContext {
    60          repo: r,
    61          kind: self.Type,
    62          argc: p.An,
    63          retc: p.Rn,
    64          argv: p.Ar,
    65          retv: p.Rr,
    66          itab: p.Ps,
    67          data: p.Pd,
    68      })
    69  }
    70  
    71  func (self *CallHandle) String() string {
    72      return fmt.Sprintf("*%#x[%s]", self.Func, self.Name())
    73  }
    74  
    75  type CallContext struct {
    76      kind CallType
    77      repo CallState
    78      itab PointerRegister
    79      data PointerRegister
    80      argc uint8
    81      retc uint8
    82      argv [8]uint8
    83      retv [8]uint8
    84  }
    85  
    86  func (self CallContext) Au(i int) uint64 {
    87      if p := self.argv[i]; p &ArgPointer != 0 {
    88          panic("invoke: invalid int argument")
    89      } else {
    90          return self.repo.Gr(GenericRegister(p & ArgMask))
    91      }
    92  }
    93  
    94  func (self CallContext) Ap(i int) unsafe.Pointer {
    95      if p := self.argv[i]; p &ArgPointer == 0 {
    96          panic("invoke: invalid pointer argument")
    97      } else {
    98          return self.repo.Pr(PointerRegister(p & ArgMask))
    99      }
   100  }
   101  
   102  func (self CallContext) Ru(i int, v uint64) {
   103      if p := self.retv[i]; p &ArgPointer != 0 {
   104          panic("invoke: invalid int return value")
   105      } else {
   106          self.repo.SetGr(GenericRegister(p &ArgMask), v)
   107      }
   108  }
   109  
   110  func (self CallContext) Rp(i int, v unsafe.Pointer) {
   111      if p := self.retv[i]; p &ArgPointer == 0 {
   112          panic("invoke: invalid pointer return value")
   113      } else {
   114          self.repo.SetPr(PointerRegister(p &ArgMask), v)
   115      }
   116  }
   117  
   118  func (self CallContext) Itab() *rt.GoItab {
   119      if self.kind != ICall {
   120          panic("invoke: itab is not available")
   121      } else {
   122          return (*rt.GoItab)(self.repo.Pr(self.itab))
   123      }
   124  }
   125  
   126  func (self CallContext) Data() unsafe.Pointer {
   127      if self.kind != ICall {
   128          panic("invoke: data is not available")
   129      } else {
   130          return self.repo.Pr(self.data)
   131      }
   132  }
   133  
   134  func (self CallContext) Verify(args string, rets string) bool {
   135      return self.verifySeq(args, self.argc, self.argv) && self.verifySeq(rets, self.retc, self.retv)
   136  }
   137  
   138  func (self CallContext) verifySeq(s string, n uint8, v [8]uint8) bool {
   139      nb := int(n)
   140      ne := len(s)
   141  
   142      /* sanity check */
   143      if ne > len(v) {
   144          panic("invoke: invalid descriptor")
   145      }
   146  
   147      /* check for value count */
   148      if nb != ne {
   149          return false
   150      }
   151  
   152      /* check for every argument */
   153      for i := 0; i < nb; i++ {
   154          switch s[i] {
   155              case 'i' : if v[i] &ArgPointer != 0 { return false }
   156              case '*' : if v[i] &ArgPointer == 0 { return false }
   157              default  : panic("invoke: invalid descriptor char: " + s[i:i + 1])
   158          }
   159      }
   160  
   161      /* all checked ok */
   162      return true
   163  }
   164  
   165  type callHandleManager struct {
   166      m       sync.RWMutex
   167      handles []*CallHandle
   168  }
   169  
   170  func (self *callHandleManager) NewWithID() *CallHandle {
   171      self.m.Lock()
   172      h := new(CallHandle)
   173      h.Id = len(self.handles)
   174      self.handles = append(self.handles, h)
   175      self.m.Unlock()
   176      return h
   177  }
   178  
   179  func (self *callHandleManager) Get(id int) (h *CallHandle) {
   180      self.m.RLock()
   181      if id >= 0 || id < len(self.handles) {
   182          h = self.handles[id]
   183      }
   184      self.m.RUnlock()
   185      return
   186  }
   187  
   188  var (
   189      funcTab = &callHandleManager{}
   190  )
   191  
   192  func LookupCall(id int64) *CallHandle {
   193      h := funcTab.Get(int(id))
   194      if h == nil {
   195          panic("invalid function ID")
   196      }
   197      return h
   198  }
   199  
   200  func RegisterICall(mt rt.Method, proxy func(CallContext)) (h *CallHandle) {
   201      h       = funcTab.NewWithID()
   202      h.Type  = ICall
   203      h.Slot  = abi.ABI.RegisterMethod(h.Id, mt)
   204      h.proxy = proxy
   205      return
   206  }
   207  
   208  func RegisterGCall(fn interface{}, proxy func(CallContext)) (h *CallHandle) {
   209      h       = funcTab.NewWithID()
   210      h.Type  = GCall
   211      h.Func  = abi.ABI.RegisterFunction(h.Id, fn)
   212      h.proxy = proxy
   213      return
   214  }
   215  
   216  func RegisterCCall(fn unsafe.Pointer, proxy func(CallContext)) (h *CallHandle) {
   217      h       = funcTab.NewWithID()
   218      h.Type  = CCall
   219      h.Func  = fn
   220      h.proxy = proxy
   221      return
   222  }