github.com/primecitizens/pcz/std@v0.2.1/rt0/rt0.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  package rt0
     5  
     6  import (
     7  	"unsafe"
     8  	_ "unsafe" // for go:linkname
     9  
    10  	"github.com/primecitizens/pcz/std/algo/rand/fastrand"
    11  	stdgo "github.com/primecitizens/pcz/std/builtin/go"
    12  	stdtype "github.com/primecitizens/pcz/std/builtin/type"
    13  	"github.com/primecitizens/pcz/std/core/abi"
    14  	"github.com/primecitizens/pcz/std/core/alloc"
    15  	"github.com/primecitizens/pcz/std/core/arch"
    16  	"github.com/primecitizens/pcz/std/core/assert"
    17  	"github.com/primecitizens/pcz/std/core/cpu"
    18  	"github.com/primecitizens/pcz/std/core/stack"
    19  	"github.com/primecitizens/pcz/std/core/thread"
    20  )
    21  
    22  var (
    23  	g0       rt0G
    24  	_g0iface stdgo.G = &g0
    25  
    26  	// mainG points to the global G that is used by:
    27  	//  - the main thread (goroutine in this std)
    28  	//	- ffi callback
    29  	//
    30  	// It points to a global rt0G by default.
    31  	//
    32  	//go:linkname mainG
    33  	mainG *stdgo.GHead = &g0.head
    34  
    35  	//go:linkname runtime_inittasks runtime.runtime_inittasks
    36  	runtime_inittasks []*abi.InitTask
    37  
    38  	// for windows, it's actually []*uint16, see ../sys/sys_windows.go
    39  	//
    40  	//go:linkname args
    41  	args []*byte
    42  
    43  	stacksize uintptr
    44  )
    45  
    46  const (
    47  	offsetsOK = unsafe.Offsetof(rt0G{}.head) == 0 &&
    48  		unsafe.Offsetof(rt0G{}.head.Stack) == 0 &&
    49  		unsafe.Offsetof(rt0G{}.head.Stack.Lo) == 0 &&
    50  		unsafe.Offsetof(rt0G{}.head.Stack.Hi) == arch.PtrSize
    51  )
    52  
    53  // rt0G is the default G implementation used for early initialization of the
    54  // program.
    55  type rt0G struct {
    56  	head  stdgo.GHead
    57  	frand fastrand.Source
    58  }
    59  
    60  //go:nosplit
    61  func (gp *rt0G) ID() uint64 { return 0 }
    62  
    63  //go:nosplit
    64  func (gp *rt0G) Rand32() uint32 { return gp.frand.Rand32() }
    65  
    66  //go:nosplit
    67  func (gp *rt0G) Rand64() uint64 { return gp.frand.Rand64() }
    68  
    69  //go:nosplit
    70  func (gp *rt0G) Status() uint32 { return stdgo.StatusGrunning }
    71  
    72  //go:nosplit
    73  func (gp *rt0G) DefaultAlloc() alloc.M { return malloc() }
    74  
    75  //go:nosplit
    76  func (gp *rt0G) PersistantAlloc() alloc.P { return palloc() }
    77  
    78  // rt0 starts the program, this function should be considered as the mstart1
    79  // in the official go runtime.
    80  //
    81  //go:nosplit
    82  //go:noinline
    83  func rt0(argc int32, argv **byte) {
    84  	if !offsetsOK {
    85  		assert.Throw("invalid", "offsets", "in", "rt0G")
    86  	}
    87  
    88  	if argc > 0 {
    89  		args = unsafe.Slice(argv, argc)
    90  	}
    91  
    92  	// TODO: get actual size of the process stack
    93  	//
    94  	// - linux/darwin: check RLIMIT_STACK
    95  	if arch.IsWasm == 1 {
    96  		stacksize = 8192
    97  	} else {
    98  		stacksize = 4 * 1024 * 1024
    99  	}
   100  
   101  	mainG.Stack.Hi = stack.GetSP()
   102  	mainG.Stack.Lo = mainG.Stack.Hi - stacksize
   103  	mainG.Stackguard0 = mainG.Stack.Lo + stack.StackGuard
   104  	mainG.Stackguard1 = mainG.Stackguard0
   105  
   106  	// NOTE: code next to this comment is critical for dwarf generation
   107  	// 		 it preserves relocation symbol `type:uintptr` for symbol `go:info.uintptr`
   108  	//
   109  	// It prevents the go tool link from throwing error:
   110  	//
   111  	// 	go:info.uintptr: unreachable sym in relocation: type:uintptr
   112  	//
   113  	// go linker doesn't mark `type:uintptr` reachable during deadcode analysis if it's
   114  	// not converted to an interface type, and that's the case for this runtime
   115  	mainG.Itab = stdtype.IfaceOf(&_g0iface).Itab
   116  
   117  	thread.SetG(mainG)
   118  
   119  	if !preinit() {
   120  		cpu.Initialize("")
   121  
   122  		for _, tsk := range runtime_inittasks {
   123  			doInit(tsk)
   124  		}
   125  
   126  		for i, iter := 0, abi.ModuleIter(0); ; i++ {
   127  			md, ok := iter.Nth(i)
   128  			if !ok {
   129  				break
   130  			}
   131  
   132  			for _, tsk := range md.InitTasks {
   133  				doInit(tsk)
   134  			}
   135  		}
   136  	}
   137  
   138  	// verify g works
   139  	if thread.G().G().Status() != stdgo.StatusGrunning {
   140  		assert.Throw("goroutine", "not", "working", "properly")
   141  	}
   142  
   143  	main()
   144  
   145  	exit0()
   146  }
   147  
   148  // `main.main` is the symbol of the main function from the main package
   149  // created by the linker.
   150  //
   151  //go:linkname main main.main
   152  func main()
   153  
   154  // TODO(toolchain): create symbol main.preinit for tasks before doInit
   155  func preinit() (skip bool) {
   156  	return false
   157  }
   158  
   159  // doInit runs all init() functions in the tsk.
   160  //
   161  //go:nosplit
   162  func doInit(tsk *abi.InitTask) {
   163  	switch tsk.State {
   164  	case 2: // fully initialized
   165  		return
   166  	case 1: // initialization in progress
   167  		assert.Throw("recursive", "call", "during", "initialization", "-", "linker", "skew")
   168  	}
   169  
   170  	// not initialized yet
   171  
   172  	tsk.State = 1 // mark in progress
   173  
   174  	if tsk.NFns == 0 {
   175  		// go toolchain should have pruned all of these in the linker.
   176  		assert.Throw("inittask", "with", "no", "functions")
   177  	}
   178  
   179  	for i := uint32(0); i < tsk.NFns; i++ {
   180  		tsk.Nth(i)()
   181  	}
   182  
   183  	tsk.State = 2 // mark done
   184  }