1  
     2  
     3  
     4  
     5  package ssagen
     6  
     7  import (
     8  	"fmt"
     9  	"internal/buildcfg"
    10  	"log"
    11  	"os"
    12  	"strings"
    13  
    14  	"cmd/compile/internal/abi"
    15  	"cmd/compile/internal/base"
    16  	"cmd/compile/internal/ir"
    17  	"cmd/compile/internal/objw"
    18  	"cmd/compile/internal/typecheck"
    19  	"cmd/compile/internal/types"
    20  	"cmd/internal/obj"
    21  	"cmd/internal/obj/wasm"
    22  
    23  	rtabi "internal/abi"
    24  )
    25  
    26  
    27  
    28  type SymABIs struct {
    29  	defs map[string]obj.ABI
    30  	refs map[string]obj.ABISet
    31  }
    32  
    33  func NewSymABIs() *SymABIs {
    34  	return &SymABIs{
    35  		defs: make(map[string]obj.ABI),
    36  		refs: make(map[string]obj.ABISet),
    37  	}
    38  }
    39  
    40  
    41  
    42  
    43  
    44  
    45  func (s *SymABIs) canonicalize(linksym string) string {
    46  	if strings.HasPrefix(linksym, `"".`) {
    47  		panic("non-canonical symbol name: " + linksym)
    48  	}
    49  	return linksym
    50  }
    51  
    52  
    53  
    54  
    55  
    56  
    57  
    58  
    59  
    60  
    61  func (s *SymABIs) ReadSymABIs(file string) {
    62  	data, err := os.ReadFile(file)
    63  	if err != nil {
    64  		log.Fatalf("-symabis: %v", err)
    65  	}
    66  
    67  	for lineNum, line := range strings.Split(string(data), "\n") {
    68  		lineNum++ 
    69  		line = strings.TrimSpace(line)
    70  		if line == "" || strings.HasPrefix(line, "#") {
    71  			continue
    72  		}
    73  
    74  		parts := strings.Fields(line)
    75  		switch parts[0] {
    76  		case "def", "ref":
    77  			
    78  			if len(parts) != 3 {
    79  				log.Fatalf(`%s:%d: invalid symabi: syntax is "%s sym abi"`, file, lineNum, parts[0])
    80  			}
    81  			sym, abistr := parts[1], parts[2]
    82  			abi, valid := obj.ParseABI(abistr)
    83  			if !valid {
    84  				log.Fatalf(`%s:%d: invalid symabi: unknown abi "%s"`, file, lineNum, abistr)
    85  			}
    86  
    87  			sym = s.canonicalize(sym)
    88  
    89  			
    90  			if parts[0] == "def" {
    91  				s.defs[sym] = abi
    92  				base.Ctxt.DwTextCount++
    93  			} else {
    94  				s.refs[sym] |= obj.ABISetOf(abi)
    95  			}
    96  		default:
    97  			log.Fatalf(`%s:%d: invalid symabi type "%s"`, file, lineNum, parts[0])
    98  		}
    99  	}
   100  }
   101  
   102  
   103  
   104  func (s *SymABIs) GenABIWrappers() {
   105  	
   106  	
   107  	
   108  	
   109  	
   110  	
   111  	
   112  	cgoExports := make(map[string][]*[]string)
   113  	for i, prag := range typecheck.Target.CgoPragmas {
   114  		switch prag[0] {
   115  		case "cgo_export_static", "cgo_export_dynamic":
   116  			symName := s.canonicalize(prag[1])
   117  			pprag := &typecheck.Target.CgoPragmas[i]
   118  			cgoExports[symName] = append(cgoExports[symName], pprag)
   119  		}
   120  	}
   121  
   122  	
   123  	
   124  	
   125  	
   126  	
   127  	for _, fn := range typecheck.Target.Funcs {
   128  		nam := fn.Nname
   129  		if ir.IsBlank(nam) {
   130  			continue
   131  		}
   132  		sym := nam.Sym()
   133  
   134  		symName := sym.Linkname
   135  		if symName == "" {
   136  			symName = sym.Pkg.Prefix + "." + sym.Name
   137  		}
   138  		symName = s.canonicalize(symName)
   139  
   140  		
   141  		defABI, hasDefABI := s.defs[symName]
   142  		if hasDefABI {
   143  			if len(fn.Body) != 0 {
   144  				base.ErrorfAt(fn.Pos(), 0, "%v defined in both Go and assembly", fn)
   145  			}
   146  			fn.ABI = defABI
   147  		}
   148  
   149  		if fn.Pragma&ir.CgoUnsafeArgs != 0 {
   150  			
   151  			
   152  			
   153  			fn.ABI = obj.ABI0
   154  			
   155  			
   156  			if sym.Linksym().IsLinkname() {
   157  				sym.LinksymABI(fn.ABI).Set(obj.AttrLinkname, true)
   158  			}
   159  		}
   160  
   161  		
   162  		
   163  		cgoExport := cgoExports[symName]
   164  		for _, pprag := range cgoExport {
   165  			
   166  			
   167  			
   168  			
   169  			
   170  			
   171  			
   172  			
   173  			
   174  			
   175  			if len(*pprag) == 2 {
   176  				*pprag = append(*pprag, (*pprag)[1])
   177  			}
   178  			
   179  			*pprag = append(*pprag, fn.ABI.String())
   180  		}
   181  
   182  		
   183  		if abis, ok := s.refs[symName]; ok {
   184  			fn.ABIRefs |= abis
   185  		}
   186  		
   187  		
   188  		
   189  		fn.ABIRefs.Set(obj.ABIInternal, true)
   190  
   191  		
   192  		
   193  		
   194  		
   195  		
   196  		
   197  		
   198  		
   199  		
   200  		
   201  		
   202  		hasBody := len(fn.Body) != 0
   203  		if sym.Linkname != "" && (hasBody || hasDefABI) && len(cgoExport) == 0 {
   204  			fn.ABIRefs |= obj.ABISetCallable
   205  		}
   206  
   207  		
   208  		
   209  		if len(cgoExport) > 0 && fn.ABIRefs&^obj.ABISetOf(fn.ABI) != 0 {
   210  			base.Fatalf("cgo exported function %v cannot have ABI wrappers", fn)
   211  		}
   212  
   213  		if !buildcfg.Experiment.RegabiWrappers {
   214  			continue
   215  		}
   216  
   217  		forEachWrapperABI(fn, makeABIWrapper)
   218  	}
   219  }
   220  
   221  func forEachWrapperABI(fn *ir.Func, cb func(fn *ir.Func, wrapperABI obj.ABI)) {
   222  	need := fn.ABIRefs &^ obj.ABISetOf(fn.ABI)
   223  	if need == 0 {
   224  		return
   225  	}
   226  
   227  	for wrapperABI := obj.ABI(0); wrapperABI < obj.ABICount; wrapperABI++ {
   228  		if !need.Get(wrapperABI) {
   229  			continue
   230  		}
   231  		cb(fn, wrapperABI)
   232  	}
   233  }
   234  
   235  
   236  
   237  func makeABIWrapper(f *ir.Func, wrapperABI obj.ABI) {
   238  	if base.Debug.ABIWrap != 0 {
   239  		fmt.Fprintf(os.Stderr, "=-= %v to %v wrapper for %v\n", wrapperABI, f.ABI, f)
   240  	}
   241  
   242  	
   243  	savepos := base.Pos
   244  	savedcurfn := ir.CurFunc
   245  
   246  	pos := base.AutogeneratedPos
   247  	base.Pos = pos
   248  
   249  	
   250  	
   251  	ft := f.Nname.Type()
   252  	if ft.NumRecvs() != 0 {
   253  		base.ErrorfAt(f.Pos(), 0, "makeABIWrapper support for wrapping methods not implemented")
   254  		return
   255  	}
   256  
   257  	
   258  	
   259  	fn := ir.NewFunc(pos, pos, f.Sym(), types.NewSignature(nil,
   260  		typecheck.NewFuncParams(ft.Params()),
   261  		typecheck.NewFuncParams(ft.Results())))
   262  	fn.ABI = wrapperABI
   263  	typecheck.DeclFunc(fn)
   264  
   265  	fn.SetABIWrapper(true)
   266  	fn.SetDupok(true)
   267  
   268  	
   269  	
   270  	
   271  	
   272  	
   273  	
   274  	
   275  	
   276  	
   277  	
   278  	
   279  	
   280  	
   281  	
   282  	
   283  	
   284  	
   285  	
   286  	
   287  	
   288  	
   289  	
   290  	
   291  	
   292  	
   293  	fn.Pragma |= ir.Nosplit
   294  
   295  	
   296  	
   297  	
   298  	
   299  	
   300  	
   301  	
   302  	
   303  	
   304  	tailcall := fn.Type().NumResults() == 0 && fn.Type().NumParams() == 0 && fn.Type().NumRecvs() == 0
   305  	if base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink {
   306  		
   307  		
   308  		tailcall = false
   309  	}
   310  	if base.Ctxt.Arch.Name == "amd64" && wrapperABI == obj.ABIInternal {
   311  		
   312  		
   313  		tailcall = false
   314  	}
   315  
   316  	var tail ir.Node
   317  	call := ir.NewCallExpr(base.Pos, ir.OCALL, f.Nname, nil)
   318  	call.Args = ir.ParamNames(fn.Type())
   319  	call.IsDDD = fn.Type().IsVariadic()
   320  	tail = call
   321  	if tailcall {
   322  		tail = ir.NewTailCallStmt(base.Pos, call)
   323  	} else if fn.Type().NumResults() > 0 {
   324  		n := ir.NewReturnStmt(base.Pos, nil)
   325  		n.Results = []ir.Node{call}
   326  		tail = n
   327  	}
   328  	fn.Body.Append(tail)
   329  
   330  	typecheck.FinishFuncBody()
   331  
   332  	ir.CurFunc = fn
   333  	typecheck.Stmts(fn.Body)
   334  
   335  	
   336  	base.Pos = savepos
   337  	ir.CurFunc = savedcurfn
   338  }
   339  
   340  
   341  
   342  
   343  func CreateWasmImportWrapper(fn *ir.Func) bool {
   344  	if fn.WasmImport == nil {
   345  		return false
   346  	}
   347  	if buildcfg.GOARCH != "wasm" {
   348  		base.FatalfAt(fn.Pos(), "CreateWasmImportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, fn)
   349  	}
   350  
   351  	ir.InitLSym(fn, true)
   352  
   353  	setupWasmImport(fn)
   354  
   355  	pp := objw.NewProgs(fn, 0)
   356  	defer pp.Free()
   357  	pp.Text.To.Type = obj.TYPE_TEXTSIZE
   358  	pp.Text.To.Val = int32(types.RoundUp(fn.Type().ArgWidth(), int64(types.RegSize)))
   359  	
   360  	pp.Text.To.Offset = 0
   361  	pp.Flush()
   362  
   363  	return true
   364  }
   365  
   366  func GenWasmExportWrapper(wrapped *ir.Func) {
   367  	if wrapped.WasmExport == nil {
   368  		return
   369  	}
   370  	if buildcfg.GOARCH != "wasm" {
   371  		base.FatalfAt(wrapped.Pos(), "GenWasmExportWrapper call not supported on %s: func was %v", buildcfg.GOARCH, wrapped)
   372  	}
   373  
   374  	pos := base.AutogeneratedPos
   375  	sym := &types.Sym{
   376  		Name:     wrapped.WasmExport.Name,
   377  		Linkname: wrapped.WasmExport.Name,
   378  	}
   379  	ft := wrapped.Nname.Type()
   380  	fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil,
   381  		typecheck.NewFuncParams(ft.Params()),
   382  		typecheck.NewFuncParams(ft.Results())))
   383  	fn.ABI = obj.ABI0 
   384  	
   385  	
   386  	
   387  	
   388  	
   389  	
   390  	if ft.ArgWidth() > rtabi.StackSmall {
   391  		base.ErrorfAt(wrapped.Pos(), 0, "wasmexport function argument too large")
   392  	}
   393  	fn.Pragma |= ir.Nosplit
   394  
   395  	ir.InitLSym(fn, true)
   396  
   397  	setupWasmExport(fn, wrapped)
   398  
   399  	pp := objw.NewProgs(fn, 0)
   400  	defer pp.Free()
   401  	
   402  	pp.Text.To.Type = obj.TYPE_TEXTSIZE
   403  	pp.Text.To.Val = int32(0)
   404  	pp.Text.To.Offset = types.RoundUp(ft.ArgWidth(), int64(types.RegSize))
   405  	
   406  	p := pp.Prog(obj.AFUNCDATA)
   407  	p.From.SetConst(rtabi.FUNCDATA_LocalsPointerMaps)
   408  	p.To.Type = obj.TYPE_MEM
   409  	p.To.Name = obj.NAME_EXTERN
   410  	p.To.Sym = base.Ctxt.Lookup("no_pointers_stackmap")
   411  	pp.Flush()
   412  	
   413  }
   414  
   415  func paramsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
   416  	wfs := make([]obj.WasmField, 0, len(abiParams))
   417  	for _, p := range abiParams {
   418  		t := p.Type
   419  		var wt obj.WasmFieldType
   420  		switch t.Kind() {
   421  		case types.TINT32, types.TUINT32:
   422  			wt = obj.WasmI32
   423  		case types.TINT64, types.TUINT64:
   424  			wt = obj.WasmI64
   425  		case types.TFLOAT32:
   426  			wt = obj.WasmF32
   427  		case types.TFLOAT64:
   428  			wt = obj.WasmF64
   429  		case types.TUNSAFEPTR, types.TUINTPTR:
   430  			wt = obj.WasmPtr
   431  		case types.TBOOL:
   432  			wt = obj.WasmBool
   433  		case types.TSTRING:
   434  			
   435  			wt = obj.WasmPtr
   436  			wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
   437  			wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result) + int64(types.PtrSize)})
   438  			continue
   439  		case types.TPTR:
   440  			if wasmElemTypeAllowed(t.Elem()) {
   441  				wt = obj.WasmPtr
   442  				break
   443  			}
   444  			fallthrough
   445  		default:
   446  			base.ErrorfAt(f.Pos(), 0, "%s: unsupported parameter type %s", pragma, t.String())
   447  		}
   448  		wfs = append(wfs, obj.WasmField{Type: wt, Offset: p.FrameOffset(result)})
   449  	}
   450  	return wfs
   451  }
   452  
   453  func resultsToWasmFields(f *ir.Func, pragma string, result *abi.ABIParamResultInfo, abiParams []abi.ABIParamAssignment) []obj.WasmField {
   454  	if len(abiParams) > 1 {
   455  		base.ErrorfAt(f.Pos(), 0, "%s: too many return values", pragma)
   456  		return nil
   457  	}
   458  	wfs := make([]obj.WasmField, len(abiParams))
   459  	for i, p := range abiParams {
   460  		t := p.Type
   461  		switch t.Kind() {
   462  		case types.TINT32, types.TUINT32:
   463  			wfs[i].Type = obj.WasmI32
   464  		case types.TINT64, types.TUINT64:
   465  			wfs[i].Type = obj.WasmI64
   466  		case types.TFLOAT32:
   467  			wfs[i].Type = obj.WasmF32
   468  		case types.TFLOAT64:
   469  			wfs[i].Type = obj.WasmF64
   470  		case types.TUNSAFEPTR, types.TUINTPTR:
   471  			wfs[i].Type = obj.WasmPtr
   472  		case types.TBOOL:
   473  			wfs[i].Type = obj.WasmBool
   474  		case types.TPTR:
   475  			if wasmElemTypeAllowed(t.Elem()) {
   476  				wfs[i].Type = obj.WasmPtr
   477  				break
   478  			}
   479  			fallthrough
   480  		default:
   481  			base.ErrorfAt(f.Pos(), 0, "%s: unsupported result type %s", pragma, t.String())
   482  		}
   483  		wfs[i].Offset = p.FrameOffset(result)
   484  	}
   485  	return wfs
   486  }
   487  
   488  
   489  
   490  
   491  func wasmElemTypeAllowed(t *types.Type) bool {
   492  	switch t.Kind() {
   493  	case types.TINT8, types.TUINT8, types.TINT16, types.TUINT16,
   494  		types.TINT32, types.TUINT32, types.TINT64, types.TUINT64,
   495  		types.TFLOAT32, types.TFLOAT64, types.TBOOL:
   496  		return true
   497  	case types.TARRAY:
   498  		return wasmElemTypeAllowed(t.Elem())
   499  	case types.TSTRUCT:
   500  		if len(t.Fields()) == 0 {
   501  			return true
   502  		}
   503  		seenHostLayout := false
   504  		for _, f := range t.Fields() {
   505  			sym := f.Type.Sym()
   506  			if sym != nil && sym.Name == "HostLayout" && sym.Pkg.Path == "structs" {
   507  				seenHostLayout = true
   508  				continue
   509  			}
   510  			if !wasmElemTypeAllowed(f.Type) {
   511  				return false
   512  			}
   513  		}
   514  		return seenHostLayout
   515  	}
   516  	
   517  	
   518  	
   519  	return false
   520  }
   521  
   522  
   523  
   524  func setupWasmImport(f *ir.Func) {
   525  	wi := obj.WasmImport{
   526  		Module: f.WasmImport.Module,
   527  		Name:   f.WasmImport.Name,
   528  	}
   529  	if wi.Module == wasm.GojsModule {
   530  		
   531  		
   532  		
   533  		
   534  		
   535  		
   536  		
   537  		
   538  		
   539  		
   540  		wi.Params = []obj.WasmField{{Type: obj.WasmI32}}
   541  	} else {
   542  		
   543  		
   544  		
   545  		
   546  		
   547  		
   548  		
   549  		
   550  		
   551  		abiConfig := AbiForBodylessFuncStackMap(f)
   552  		abiInfo := abiConfig.ABIAnalyzeFuncType(f.Type())
   553  		wi.Params = paramsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.InParams())
   554  		wi.Results = resultsToWasmFields(f, "go:wasmimport", abiInfo, abiInfo.OutParams())
   555  	}
   556  	f.LSym.Func().WasmImport = &wi
   557  }
   558  
   559  
   560  
   561  func setupWasmExport(f, wrapped *ir.Func) {
   562  	we := obj.WasmExport{
   563  		WrappedSym: wrapped.LSym,
   564  	}
   565  	abiConfig := AbiForBodylessFuncStackMap(wrapped)
   566  	abiInfo := abiConfig.ABIAnalyzeFuncType(wrapped.Type())
   567  	we.Params = paramsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.InParams())
   568  	we.Results = resultsToWasmFields(wrapped, "go:wasmexport", abiInfo, abiInfo.OutParams())
   569  	f.LSym.Func().WasmExport = &we
   570  }
   571  
View as plain text