compiler: make runtime.makeGoroutine AVR compatible
Previously it would use a bitcast, which cannot directly be used on AVR
because functions live in a different address space on AVR. To fix this,
use a ptrtoint/inttoptr pair.
This allows testdata/coroutines.go to be compiled, but due to what
appears to be an LLVM bug cannot be optimized and codegen'ed:
    tinygo: /home/ayke/src/github.com/tinygo-org/tinygo/llvm-project/llvm/lib/IR/Constants.cpp:1776: static llvm::Constant *llvm::ConstantExpr::getBitCast(llvm::Constant *, llvm::Type *, bool): Assertion `CastInst::castIsValid(Instruction::BitCast, C, DstTy) && "Invalid constantexpr bitcast!"' failed.
This happens as one of the function passes after the TinyGo passes and
after the module has been verified so most likely it is a bug somewhere
in LLVM.
			
			
Этот коммит содержится в:
		
							родитель
							
								
									92206558fb
								
							
						
					
					
						коммит
						562ad740da
					
				
					 3 изменённых файлов: 27 добавлений и 19 удалений
				
			
		| 
						 | 
				
			
			@ -1045,9 +1045,9 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
 | 
			
		|||
		// interprocedural optimizations. For example, heap-to-stack
 | 
			
		||||
		// transformations are not sound as goroutines can outlive their parent.
 | 
			
		||||
		calleeType := calleeFn.LLVMFn.Type()
 | 
			
		||||
		calleeValue := c.builder.CreateBitCast(calleeFn.LLVMFn, c.i8ptrType, "")
 | 
			
		||||
		calleeValue := c.builder.CreatePtrToInt(calleeFn.LLVMFn, c.uintptrType, "")
 | 
			
		||||
		calleeValue = c.createRuntimeCall("makeGoroutine", []llvm.Value{calleeValue}, "")
 | 
			
		||||
		calleeValue = c.builder.CreateBitCast(calleeValue, calleeType, "")
 | 
			
		||||
		calleeValue = c.builder.CreateIntToPtr(calleeValue, calleeType, "")
 | 
			
		||||
 | 
			
		||||
		// Get all function parameters to pass to the goroutine.
 | 
			
		||||
		var params []llvm.Value
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -211,17 +211,25 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
 | 
			
		|||
 | 
			
		||||
		// Add all callees to the worklist.
 | 
			
		||||
		for _, use := range getUses(f) {
 | 
			
		||||
			if use.IsConstant() && use.Opcode() == llvm.BitCast {
 | 
			
		||||
				bitcastUses := getUses(use)
 | 
			
		||||
				for _, call := range bitcastUses {
 | 
			
		||||
			if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
 | 
			
		||||
				for _, call := range getUses(use) {
 | 
			
		||||
					if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
 | 
			
		||||
						return false, errors.New("async function " + f.Name() + " incorrectly used in bitcast, expected runtime.makeGoroutine")
 | 
			
		||||
						return false, errors.New("async function " + f.Name() + " incorrectly used in ptrtoint, expected runtime.makeGoroutine")
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				// This is a go statement. Do not mark the parent as async, as
 | 
			
		||||
				// starting a goroutine is not a blocking operation.
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if use.IsConstant() && use.Opcode() == llvm.BitCast {
 | 
			
		||||
				// Not sure why this const bitcast is here but as long as it
 | 
			
		||||
				// has no uses it can be ignored, I guess?
 | 
			
		||||
				// I think it was created for the runtime.isnil check but
 | 
			
		||||
				// somehow wasn't removed when all these checks are removed.
 | 
			
		||||
				if len(getUses(use)) == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if use.IsACallInst().IsNil() {
 | 
			
		||||
				// Not a call instruction. Maybe a store to a global? In any
 | 
			
		||||
				// case, this requires support for async calls across function
 | 
			
		||||
| 
						 | 
				
			
			@ -250,12 +258,12 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
 | 
			
		|||
		// goroutine is not async (does not do any blocking operation), no
 | 
			
		||||
		// scheduler is necessary as it can be called directly.
 | 
			
		||||
		for _, use := range getUses(makeGoroutine) {
 | 
			
		||||
			// Input param must be const bitcast of function.
 | 
			
		||||
			bitcast := use.Operand(0)
 | 
			
		||||
			if !bitcast.IsConstant() || bitcast.Opcode() != llvm.BitCast {
 | 
			
		||||
				panic("expected const bitcast operand of runtime.makeGoroutine")
 | 
			
		||||
			// Input param must be const ptrtoint of function.
 | 
			
		||||
			ptrtoint := use.Operand(0)
 | 
			
		||||
			if !ptrtoint.IsConstant() || ptrtoint.Opcode() != llvm.PtrToInt {
 | 
			
		||||
				panic("expected const ptrtoint operand of runtime.makeGoroutine")
 | 
			
		||||
			}
 | 
			
		||||
			goroutine := bitcast.Operand(0)
 | 
			
		||||
			goroutine := ptrtoint.Operand(0)
 | 
			
		||||
			if _, ok := asyncFuncs[goroutine]; ok {
 | 
			
		||||
				needsScheduler = true
 | 
			
		||||
				break
 | 
			
		||||
| 
						 | 
				
			
			@ -571,14 +579,14 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {
 | 
			
		|||
 | 
			
		||||
	makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
 | 
			
		||||
	for _, goroutine := range getUses(makeGoroutine) {
 | 
			
		||||
		bitcastIn := goroutine.Operand(0)
 | 
			
		||||
		origFunc := bitcastIn.Operand(0)
 | 
			
		||||
		ptrtointIn := goroutine.Operand(0)
 | 
			
		||||
		origFunc := ptrtointIn.Operand(0)
 | 
			
		||||
		uses := getUses(goroutine)
 | 
			
		||||
		if len(uses) != 1 || uses[0].IsABitCastInst().IsNil() {
 | 
			
		||||
			return errors.New("expected exactly 1 bitcast use of runtime.makeGoroutine")
 | 
			
		||||
		if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
 | 
			
		||||
			return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
 | 
			
		||||
		}
 | 
			
		||||
		bitcastOut := uses[0]
 | 
			
		||||
		uses = getUses(bitcastOut)
 | 
			
		||||
		inttoptrOut := uses[0]
 | 
			
		||||
		uses = getUses(inttoptrOut)
 | 
			
		||||
		if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
 | 
			
		||||
			return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -593,7 +601,7 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {
 | 
			
		|||
		c.builder.SetInsertPointBefore(realCall)
 | 
			
		||||
		c.builder.CreateCall(origFunc, params, "")
 | 
			
		||||
		realCall.EraseFromParentAsInstruction()
 | 
			
		||||
		bitcastOut.EraseFromParentAsInstruction()
 | 
			
		||||
		inttoptrOut.EraseFromParentAsInstruction()
 | 
			
		||||
		goroutine.EraseFromParentAsInstruction()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ func (t *coroutine) promise() *taskState {
 | 
			
		|||
	return (*taskState)(t._promise(int32(unsafe.Alignof(taskState{})), false))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeGoroutine(*uint8) *uint8
 | 
			
		||||
func makeGoroutine(uintptr) uintptr
 | 
			
		||||
 | 
			
		||||
// Compiler stub to get the current goroutine. Calls to this function are
 | 
			
		||||
// removed in the goroutine lowering pass.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче