Swiftc traps when compiling stdlib for Harvard Architecture processors

Hi,

I'm getting a crash here...

   2599	static void emitRetconCoroutineEntry(IRGenFunction &IGF,
   2600	                                     CanSILFunctionType fnType,
   2601	                                     Explosion &allParamValues,
   2602	                                     llvm::Intrinsic::ID idIntrinsic,
   2603	                                     Size bufferSize,
   2604	                                     Alignment bufferAlignment) {
   2605	  auto prototype =
-> 2606	    IGF.IGM.getOpaquePtr(IGF.IGM.getAddrOfContinuationPrototype(fnType));
   2607
   2608	  // Use malloc and free as our allocator.
   2609	  auto allocFn = IGF.IGM.getOpaquePtr(IGF.IGM.getMallocFn());
   2610	  auto deallocFn = IGF.IGM.getOpaquePtr(IGF.IGM.getFreeFn());

...one frame lower...

   1089	llvm::Constant *IRGenModule::getOpaquePtr(llvm::Constant *ptr) {
-> 1090	  return llvm::ConstantExpr::getBitCast(ptr, Int8PtrTy);
   1091	}

...the constant in question is declare swiftcc void @"$sSfBf32_IetMAlYl_TC"(i8* noalias dereferenceable(8) %0, i1 %1) addrspace(1) #0

which is of type void (i8*, i1) addrspace(1)* and so can't be cast to i8* because they are in different address spaces. (Functions on Harvard Arch... or at least on my platform... live in address space 1.)

I've had somewhat similar issues in a couple of other places and in those places was able to change the cast to i8 addrspace(1)* which llvm can cast no problem. I have added another union to IRGenModule to make this easy...

  union {
    llvm::PointerType *FunctionPtrTy;
    llvm::PointerType *Int8ProgramSpacePtrTy; /// i8* in same address space as programs
  };

...which I initialise to the type i8 addrspace(1)*.

I can change IRGenModule::getOpaquePtr on my platform to cast to this type, which would probably solve this one crash.

But I'm a bit unsure of the bigger picture here and what makes sense. Opaque pointers are probably created and used across the compiler in situations where they are pointing to data (normal buffers, references, etc.) and even in this case, what happens to the pointer being created? If it's then added to another i8* pointer or some other operation, then the crash will just move one instruction further down the SIL function being lowered.

Another alternative is to change emitYieldOnceCoroutineEntry to not use opaque ptrs but something similar and program space (addrespace 1) scoped instead.

I'm a bit unsure how best to proceed. Can anyone offer advice?

Carl

This might actually be a problem for ARM Cortex M0 as well, when I get round to that platform (soon!)

I dont have an immediate answer for you, but there must be some answer because Wasm is also a Harvard architecture. I suggest seeing what they've done in their fork.

1 Like

Oh, I didn't know that! Where does their fork live?

Looks like GitHub - swiftwasm/swift: WebAssembly support for the Swift programming language is still the home for the fork, and they’ve got links out to various community sites where you could ask questions. (Several of the contributors are here too but it feels rude to directly summon them into a conversation by tag!)

1 Like

Thanks so much @jrose ! I’ll check that out and if I get a chance I’ll try to come back to report what I find in case people are searching this topic in future.

3 Likes

I'll be honest, I can't work out exactly how wasm are handling it. I think it might be because their llvm backend target datalayout doesn't use P1 to put programs in address space 1. They must have a different approach to pointers.

In the end I just made this change, which seems to work...

llvm::Constant *IRGenModule::getOpaquePtr(llvm::Constant *ptr) {
  return llvm::ConstantExpr::getAddrSpaceCast(ptr, Int8PtrTy);
}

...emitting an AddrSpaceCast instruction instead. That should work cross platform. And I assume however this coroutine is used, the opaque pointers will be turned back to function pointers at some point so it should all work.