Replace _llvm_* workaround in the scheduler with //go:linkname
This also removes the need for the _llvm_ special case in the compiler. And it makes the scheduler code a whole lot nicer!
Этот коммит содержится в:
		
							родитель
							
								
									771f23e320
								
							
						
					
					
						коммит
						74bd378c29
					
				
					 2 изменённых файлов: 55 добавлений и 59 удалений
				
			
		
							
								
								
									
										7
									
								
								ir.go
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								ir.go
									
										
									
									
									
								
							|  | @ -176,12 +176,7 @@ func (f *Function) LinkName() string { | ||||||
| 			// Name CGo functions directly. | 			// Name CGo functions directly. | ||||||
| 			return name | 			return name | ||||||
| 		} else { | 		} else { | ||||||
| 			name := f.fn.RelString(nil) | 			return f.fn.RelString(nil) | ||||||
| 			if f.fn.Pkg.Pkg.Path() == "runtime" && strings.HasPrefix(f.fn.Name(), "_llvm_") { |  | ||||||
| 				// Special case for LLVM intrinsics in the runtime. |  | ||||||
| 				name = "llvm." + strings.Replace(f.fn.Name()[len("_llvm_"):], "_", ".", -1) |  | ||||||
| 			} |  | ||||||
| 			return name |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -25,18 +25,36 @@ import ( | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // A coroutine instance, wrapped here to provide some type safety. The value | ||||||
|  | // must not be used directly, it is meant to be used as an opaque *i8 in LLVM. | ||||||
|  | type coroutine uint8 | ||||||
|  | 
 | ||||||
|  | //go:linkname resume llvm.coro.resume | ||||||
|  | func (t *coroutine) resume() | ||||||
|  | 
 | ||||||
|  | //go:linkname destroy llvm.coro.destroy | ||||||
|  | func (t *coroutine) destroy() | ||||||
|  | 
 | ||||||
|  | //go:linkname done llvm.coro.done | ||||||
|  | func (t *coroutine) done() bool | ||||||
|  | 
 | ||||||
|  | //go:linkname _promise llvm.coro.promise | ||||||
|  | func (t *coroutine) _promise(alignment int32, from bool) unsafe.Pointer | ||||||
|  | 
 | ||||||
|  | // Get the promise belonging to a task. | ||||||
|  | func (t *coroutine) promise() *taskState { | ||||||
|  | 	return (*taskState)(t._promise(4, false)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // State/promise of a task. Internally represented as: | // State/promise of a task. Internally represented as: | ||||||
| // | // | ||||||
| //     {i8 state, i32 data, i8* next} | //     {i8 state, i32 data, i8* next} | ||||||
| type taskState struct { | type taskState struct { | ||||||
| 	state uint8 | 	state uint8 | ||||||
| 	data  uint32 | 	data  uint32 | ||||||
| 	next  taskInstance | 	next  *coroutine | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Pointer to a task. Wrap unsafe.Pointer to provide some sort of type safety. |  | ||||||
| type taskInstance unsafe.Pointer |  | ||||||
| 
 |  | ||||||
| // Various states a task can be in. Not always updated (especially | // Various states a task can be in. Not always updated (especially | ||||||
| // TASK_STATE_RUNNABLE). | // TASK_STATE_RUNNABLE). | ||||||
| const ( | const ( | ||||||
|  | @ -50,44 +68,27 @@ const ( | ||||||
| // TODO: runqueueFront can be removed by making the run queue a circular linked | // TODO: runqueueFront can be removed by making the run queue a circular linked | ||||||
| // list. The runqueueBack will simply refer to the front in the 'next' pointer. | // list. The runqueueBack will simply refer to the front in the 'next' pointer. | ||||||
| var ( | var ( | ||||||
| 	runqueueFront      taskInstance | 	runqueueFront      *coroutine | ||||||
| 	runqueueBack       taskInstance | 	runqueueBack       *coroutine | ||||||
| 	sleepQueue         taskInstance | 	sleepQueue         *coroutine | ||||||
| 	sleepQueueBaseTime uint64 | 	sleepQueueBaseTime uint64 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Translated to void @llvm.coro.resume(i8*). |  | ||||||
| func _llvm_coro_resume(taskInstance) |  | ||||||
| 
 |  | ||||||
| // Translated to void @llvm.coro.destroy(i8*). |  | ||||||
| func _llvm_coro_destroy(taskInstance) |  | ||||||
| 
 |  | ||||||
| // Translated to i1 @llvm.coro.done(i8*). |  | ||||||
| func _llvm_coro_done(taskInstance) bool |  | ||||||
| 
 |  | ||||||
| // Translated to i8* @llvm.coro.promise(i8*, i32, i1). |  | ||||||
| func _llvm_coro_promise(taskInstance, int32, bool) unsafe.Pointer |  | ||||||
| 
 |  | ||||||
| // Get the promise belonging to a task. |  | ||||||
| func taskPromise(t taskInstance) *taskState { |  | ||||||
| 	return (*taskState)(_llvm_coro_promise(t, 4, false)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Simple logging, for debugging. | // Simple logging, for debugging. | ||||||
| func scheduleLog(msg string) { | func scheduleLog(msg string) { | ||||||
| 	//println(msg) | 	//println(msg) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Simple logging with a task pointer, for debugging. | // Simple logging with a task pointer, for debugging. | ||||||
| func scheduleLogTask(msg string, t taskInstance) { | func scheduleLogTask(msg string, t *coroutine) { | ||||||
| 	//println(msg, t) | 	//println(msg, t) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Set the task state to sleep for a given time. | // Set the task state to sleep for a given time. | ||||||
| // | // | ||||||
| // This is a compiler intrinsic. | // This is a compiler intrinsic. | ||||||
| func sleepTask(caller taskInstance, duration Duration) { | func sleepTask(caller *coroutine, duration Duration) { | ||||||
| 	promise := taskPromise(caller) | 	promise := caller.promise() | ||||||
| 	promise.state = TASK_STATE_SLEEP | 	promise.state = TASK_STATE_SLEEP | ||||||
| 	promise.data = uint32(duration) // TODO: longer durations | 	promise.data = uint32(duration) // TODO: longer durations | ||||||
| } | } | ||||||
|  | @ -96,22 +97,22 @@ func sleepTask(caller taskInstance, duration Duration) { | ||||||
| // will be removed from the runqueue and be rescheduled by the callee. | // will be removed from the runqueue and be rescheduled by the callee. | ||||||
| // | // | ||||||
| // This is a compiler intrinsic. | // This is a compiler intrinsic. | ||||||
| func waitForAsyncCall(caller taskInstance) { | func waitForAsyncCall(caller *coroutine) { | ||||||
| 	promise := taskPromise(caller) | 	promise := caller.promise() | ||||||
| 	promise.state = TASK_STATE_CALL | 	promise.state = TASK_STATE_CALL | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Add a task to the runnable or sleep queue, depending on the state. | // Add a task to the runnable or sleep queue, depending on the state. | ||||||
| // | // | ||||||
| // This is a compiler intrinsic. | // This is a compiler intrinsic. | ||||||
| func scheduleTask(t taskInstance) { | func scheduleTask(t *coroutine) { | ||||||
| 	if t == nil { | 	if t == nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	scheduleLogTask("  schedule task:", t) | 	scheduleLogTask("  schedule task:", t) | ||||||
| 	// See what we should do with this task: try to execute it directly | 	// See what we should do with this task: try to execute it directly | ||||||
| 	// again or let it sleep for a bit. | 	// again or let it sleep for a bit. | ||||||
| 	promise := taskPromise(t) | 	promise := t.promise() | ||||||
| 	if promise.state == TASK_STATE_CALL { | 	if promise.state == TASK_STATE_CALL { | ||||||
| 		return // calling an async task, the subroutine will re-active the parent | 		return // calling an async task, the subroutine will re-active the parent | ||||||
| 	} else if promise.state == TASK_STATE_SLEEP && promise.data != 0 { | 	} else if promise.state == TASK_STATE_SLEEP && promise.data != 0 { | ||||||
|  | @ -123,30 +124,30 @@ func scheduleTask(t taskInstance) { | ||||||
| 
 | 
 | ||||||
| // Add this task to the end of the run queue. May also destroy the task if it's | // Add this task to the end of the run queue. May also destroy the task if it's | ||||||
| // done. | // done. | ||||||
| func pushTask(t taskInstance) { | func pushTask(t *coroutine) { | ||||||
| 	if _llvm_coro_done(t) { | 	if t.done() { | ||||||
| 		scheduleLogTask("  destroy task:", t) | 		scheduleLogTask("  destroy task:", t) | ||||||
| 		_llvm_coro_destroy(t) | 		t.destroy() | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if runqueueBack == nil { // empty runqueue | 	if runqueueBack == nil { // empty runqueue | ||||||
| 		runqueueBack = t | 		runqueueBack = t | ||||||
| 		runqueueFront = t | 		runqueueFront = t | ||||||
| 	} else { | 	} else { | ||||||
| 		lastTaskPromise := taskPromise(runqueueBack) | 		lastTaskPromise := runqueueBack.promise() | ||||||
| 		lastTaskPromise.next = t | 		lastTaskPromise.next = t | ||||||
| 		runqueueBack = t | 		runqueueBack = t | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Get a task from the front of the run queue. May return nil if there is none. | // Get a task from the front of the run queue. May return nil if there is none. | ||||||
| func popTask() taskInstance { | func popTask() *coroutine { | ||||||
| 	t := runqueueFront | 	t := runqueueFront | ||||||
| 	if t == nil { | 	if t == nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	scheduleLogTask("    popTask:", t) | 	scheduleLogTask("    popTask:", t) | ||||||
| 	promise := taskPromise(t) | 	promise := t.promise() | ||||||
| 	runqueueFront = promise.next | 	runqueueFront = promise.next | ||||||
| 	if runqueueFront == nil { | 	if runqueueFront == nil { | ||||||
| 		runqueueBack = nil | 		runqueueBack = nil | ||||||
|  | @ -156,7 +157,7 @@ func popTask() taskInstance { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Add this task to the sleep queue, assuming its state is set to sleeping. | // Add this task to the sleep queue, assuming its state is set to sleeping. | ||||||
| func addSleepTask(t taskInstance) { | func addSleepTask(t *coroutine) { | ||||||
| 	now := monotime() | 	now := monotime() | ||||||
| 	if sleepQueue == nil { | 	if sleepQueue == nil { | ||||||
| 		scheduleLog("  -> sleep new queue") | 		scheduleLog("  -> sleep new queue") | ||||||
|  | @ -167,12 +168,12 @@ func addSleepTask(t taskInstance) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Make sure promise.data is relative to the queue time base. | 	// Make sure promise.data is relative to the queue time base. | ||||||
| 	promise := taskPromise(t) | 	promise := t.promise() | ||||||
| 
 | 
 | ||||||
| 	// Insert at front of sleep queue. | 	// Insert at front of sleep queue. | ||||||
| 	if promise.data < taskPromise(sleepQueue).data { | 	if promise.data < sleepQueue.promise().data { | ||||||
| 		scheduleLog("  -> sleep at start") | 		scheduleLog("  -> sleep at start") | ||||||
| 		taskPromise(sleepQueue).data -= promise.data | 		sleepQueue.promise().data -= promise.data | ||||||
| 		promise.next = sleepQueue | 		promise.next = sleepQueue | ||||||
| 		sleepQueue = t | 		sleepQueue = t | ||||||
| 		return | 		return | ||||||
|  | @ -181,26 +182,26 @@ func addSleepTask(t taskInstance) { | ||||||
| 	// Add to sleep queue (in the middle or at the end). | 	// Add to sleep queue (in the middle or at the end). | ||||||
| 	queueIndex := sleepQueue | 	queueIndex := sleepQueue | ||||||
| 	for { | 	for { | ||||||
| 		promise.data -= taskPromise(queueIndex).data | 		promise.data -= queueIndex.promise().data | ||||||
| 		if taskPromise(queueIndex).next == nil || taskPromise(queueIndex).data > promise.data { | 		if queueIndex.promise().next == nil || queueIndex.promise().data > promise.data { | ||||||
| 			if taskPromise(queueIndex).next == nil { | 			if queueIndex.promise().next == nil { | ||||||
| 				scheduleLog("  -> sleep at end") | 				scheduleLog("  -> sleep at end") | ||||||
| 				promise.next = nil | 				promise.next = nil | ||||||
| 			} else { | 			} else { | ||||||
| 				scheduleLog("  -> sleep in middle") | 				scheduleLog("  -> sleep in middle") | ||||||
| 				promise.next = taskPromise(queueIndex).next | 				promise.next = queueIndex.promise().next | ||||||
| 				taskPromise(promise.next).data -= promise.data | 				promise.next.promise().data -= promise.data | ||||||
| 			} | 			} | ||||||
| 			taskPromise(queueIndex).next = t | 			queueIndex.promise().next = t | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		queueIndex = taskPromise(queueIndex).next | 		queueIndex = queueIndex.promise().next | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Run the scheduler until all tasks have finished. | // Run the scheduler until all tasks have finished. | ||||||
| // It takes an initial task (main.main) to bootstrap. | // It takes an initial task (main.main) to bootstrap. | ||||||
| func scheduler(main taskInstance) { | func scheduler(main *coroutine) { | ||||||
| 	// Initial task. | 	// Initial task. | ||||||
| 	scheduleTask(main) | 	scheduleTask(main) | ||||||
| 
 | 
 | ||||||
|  | @ -211,10 +212,10 @@ func scheduler(main taskInstance) { | ||||||
| 
 | 
 | ||||||
| 		// Add tasks that are done sleeping to the end of the runqueue so they | 		// Add tasks that are done sleeping to the end of the runqueue so they | ||||||
| 		// will be executed soon. | 		// will be executed soon. | ||||||
| 		if sleepQueue != nil && now-sleepQueueBaseTime >= uint64(taskPromise(sleepQueue).data) { | 		if sleepQueue != nil && now-sleepQueueBaseTime >= uint64(sleepQueue.promise().data) { | ||||||
| 			scheduleLog("  run <- sleep") | 			scheduleLog("  run <- sleep") | ||||||
| 			t := sleepQueue | 			t := sleepQueue | ||||||
| 			promise := taskPromise(t) | 			promise := t.promise() | ||||||
| 			sleepQueueBaseTime += uint64(promise.data) | 			sleepQueueBaseTime += uint64(promise.data) | ||||||
| 			sleepQueue = promise.next | 			sleepQueue = promise.next | ||||||
| 			promise.next = nil | 			promise.next = nil | ||||||
|  | @ -233,14 +234,14 @@ func scheduler(main taskInstance) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			scheduleLog("  sleeping...") | 			scheduleLog("  sleeping...") | ||||||
| 			timeLeft := uint64(taskPromise(sleepQueue).data) - (now - sleepQueueBaseTime) | 			timeLeft := uint64(sleepQueue.promise().data) - (now - sleepQueueBaseTime) | ||||||
| 			sleep(Duration(timeLeft)) | 			sleep(Duration(timeLeft)) | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Run the given task. | 		// Run the given task. | ||||||
| 		scheduleLogTask("  run:", t) | 		scheduleLogTask("  run:", t) | ||||||
| 		_llvm_coro_resume(t) | 		t.resume() | ||||||
| 
 | 
 | ||||||
| 		// Add the just resumed task to the run queue or the sleep queue. | 		// Add the just resumed task to the run queue or the sleep queue. | ||||||
| 		scheduleTask(t) | 		scheduleTask(t) | ||||||
|  |  | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Ayke van Laethem
						Ayke van Laethem