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
|
// interprocedural optimizations. For example, heap-to-stack
|
||||||
// transformations are not sound as goroutines can outlive their parent.
|
// transformations are not sound as goroutines can outlive their parent.
|
||||||
calleeType := calleeFn.LLVMFn.Type()
|
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.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.
|
// Get all function parameters to pass to the goroutine.
|
||||||
var params []llvm.Value
|
var params []llvm.Value
|
||||||
|
|
|
@ -211,17 +211,25 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
|
|
||||||
// Add all callees to the worklist.
|
// Add all callees to the worklist.
|
||||||
for _, use := range getUses(f) {
|
for _, use := range getUses(f) {
|
||||||
if use.IsConstant() && use.Opcode() == llvm.BitCast {
|
if use.IsConstant() && use.Opcode() == llvm.PtrToInt {
|
||||||
bitcastUses := getUses(use)
|
for _, call := range getUses(use) {
|
||||||
for _, call := range bitcastUses {
|
|
||||||
if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
|
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
|
// This is a go statement. Do not mark the parent as async, as
|
||||||
// starting a goroutine is not a blocking operation.
|
// starting a goroutine is not a blocking operation.
|
||||||
continue
|
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() {
|
if use.IsACallInst().IsNil() {
|
||||||
// Not a call instruction. Maybe a store to a global? In any
|
// Not a call instruction. Maybe a store to a global? In any
|
||||||
// case, this requires support for async calls across function
|
// 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
|
// goroutine is not async (does not do any blocking operation), no
|
||||||
// scheduler is necessary as it can be called directly.
|
// scheduler is necessary as it can be called directly.
|
||||||
for _, use := range getUses(makeGoroutine) {
|
for _, use := range getUses(makeGoroutine) {
|
||||||
// Input param must be const bitcast of function.
|
// Input param must be const ptrtoint of function.
|
||||||
bitcast := use.Operand(0)
|
ptrtoint := use.Operand(0)
|
||||||
if !bitcast.IsConstant() || bitcast.Opcode() != llvm.BitCast {
|
if !ptrtoint.IsConstant() || ptrtoint.Opcode() != llvm.PtrToInt {
|
||||||
panic("expected const bitcast operand of runtime.makeGoroutine")
|
panic("expected const ptrtoint operand of runtime.makeGoroutine")
|
||||||
}
|
}
|
||||||
goroutine := bitcast.Operand(0)
|
goroutine := ptrtoint.Operand(0)
|
||||||
if _, ok := asyncFuncs[goroutine]; ok {
|
if _, ok := asyncFuncs[goroutine]; ok {
|
||||||
needsScheduler = true
|
needsScheduler = true
|
||||||
break
|
break
|
||||||
|
@ -571,14 +579,14 @@ func (c *Compiler) lowerMakeGoroutineCalls() error {
|
||||||
|
|
||||||
makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
|
makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
|
||||||
for _, goroutine := range getUses(makeGoroutine) {
|
for _, goroutine := range getUses(makeGoroutine) {
|
||||||
bitcastIn := goroutine.Operand(0)
|
ptrtointIn := goroutine.Operand(0)
|
||||||
origFunc := bitcastIn.Operand(0)
|
origFunc := ptrtointIn.Operand(0)
|
||||||
uses := getUses(goroutine)
|
uses := getUses(goroutine)
|
||||||
if len(uses) != 1 || uses[0].IsABitCastInst().IsNil() {
|
if len(uses) != 1 || uses[0].IsAIntToPtrInst().IsNil() {
|
||||||
return errors.New("expected exactly 1 bitcast use of runtime.makeGoroutine")
|
return errors.New("expected exactly 1 inttoptr use of runtime.makeGoroutine")
|
||||||
}
|
}
|
||||||
bitcastOut := uses[0]
|
inttoptrOut := uses[0]
|
||||||
uses = getUses(bitcastOut)
|
uses = getUses(inttoptrOut)
|
||||||
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
|
||||||
return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
|
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.SetInsertPointBefore(realCall)
|
||||||
c.builder.CreateCall(origFunc, params, "")
|
c.builder.CreateCall(origFunc, params, "")
|
||||||
realCall.EraseFromParentAsInstruction()
|
realCall.EraseFromParentAsInstruction()
|
||||||
bitcastOut.EraseFromParentAsInstruction()
|
inttoptrOut.EraseFromParentAsInstruction()
|
||||||
goroutine.EraseFromParentAsInstruction()
|
goroutine.EraseFromParentAsInstruction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (t *coroutine) promise() *taskState {
|
||||||
return (*taskState)(t._promise(int32(unsafe.Alignof(taskState{})), false))
|
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
|
// Compiler stub to get the current goroutine. Calls to this function are
|
||||||
// removed in the goroutine lowering pass.
|
// removed in the goroutine lowering pass.
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче