Skip to content

`foreign import prim` is not as expressive as out_of_line primops

Motivation

This is the future work left by !1330 (closed), and described in Note [When do out-of-line primops go in primops.txt.pp]. In short, it would be nice to make external only primops just foreign calls to C--, since they don't need special compiler support.

Proposal

There are for axes on which foreign import prim needs to be made more flexible.

  • No polymorphism in type. Should be no restriction beyond normal Haskell I think. It is up to the C-- to do things correctly.
  • can_fail = False. Needs to sometimes be true.
  • has_side_effects = True. Needs to sometimes be false.
  • strictness = <default> this is the hardest to fix, as we can not just allow arbitrary code like genprimop does. But we can defunctionalize the functions we need in practice at least (far fewer than number of foreign import prims). @simonpj mentions using wired-in IDs for this in !1330 (comment 211428).

Some syntax similar to genprimop

foreign import prim "symbol" Type where
  identifier = identifier

where the LHS is the properties, and the right hand side is True or false False, except for strictness where it is the wired-in strictness function.

Primop Census

Here's a census of primops that could in principle be turned into foreign import prim. First I collected all primops whose data constructors are not used inside the compiler:

for x in $(cat primops); do [ (git grep -l $x compiler | wc -l) = 1 ] && echo "$x"; done

This yields a list of primops which don't need bespoke compiler support.

Next I marked any current restrictions for

Primop Strictness Type Signature Can fail Side effect free
DoubleDecode_2IntOp X
DoubleDecode_Int64Op X
FloatDecode_IntOp X
UnsafeThawArrayOp X
CasArrayOp X
UnsafeThawSmallArrayOp X
CasSmallArrayOp X
NewPinnedByteArrayOp_Char X
NewAlignedPinnedByteArrayOp_Char X
MutableByteArrayIsPinnedOp X X
ByteArrayIsPinnedOp X
ShrinkMutableByteArrayOp_Char X
ResizeMutableByteArrayOp_Char X
NewArrayArrayOp X
NewMutVarOp X
AtomicModifyMutVar2Op X X
AtomicModifyMutVar_Op X X
CasMutVarOp X X
CatchOp X X
RaiseOp X X
RaiseIOOp X X
MaskAsyncExceptionsOp X X
MaskUninterruptibleOp X X
UnmaskAsyncExceptionsOp X X
MaskStatus X
AtomicallyOp X X
RetryOp X X
CatchRetryOp X X
CatchSTMOp X X
NewTVarOp X
ReadTVarOp X
ReadTVarIOOp X
WriteTVarOp X
NewMVarOp X
TakeMVarOp X
TryTakeMVarOp X
PutMVarOp X
TryPutMVarOp X
ReadMVarOp X
TryReadMVarOp X
IsEmptyMVarOp X
DelayOp X
WaitReadOp X
WaitWriteOp X
ForkOp X
ForkOnOp X
KillThreadOp X
YieldOp X
LabelThreadOp X
IsCurrentThreadBoundOp
NoDuplicateOp X
ThreadStatusOp
MkWeakOp X
MkWeakNoFinalizerOp X
AddCFinalizerToWeakOp
DeRefWeakOp X
FinalizeWeakOp X
MakeStablePtrOp X
DeRefStablePtrOp X
MakeStableNameOp X
CompactNewOp
CompactResizeOp X
CompactContainsOp
CompactContainsAnyOp
CompactGetFirstBlockOp
CompactGetNextBlockOp
CompactAllocateBlockOp
CompactFixupPointersOp
CompactAdd X
CompactAddWithSharing X
CompactSize
GetSparkOp X
NumSparks
MkApUpd0_Op X
NewBCOOp X
UnpackClosureOp X
ClosureSizeOp X
GetApStackValOp X
ClearCCSOp X
TraceEventOp X
TraceEventBinaryOp X
TraceMarkerOp X
GetThreadAllocationCounter
SetThreadAllocationCounter X

That's 84 out of 579 primops.

Edited by John Ericson
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information